Skip to content

Commit c76260f

Browse files
committed
Basic Implementation of reactions between aspects in the vessel
1 parent b6a215e commit c76260f

17 files changed

Lines changed: 544 additions & 26 deletions

thaumaturge/src/main/java/dev/overgrown/thaumaturge/Thaumaturge.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dev.overgrown.thaumaturge;
22

3+
import dev.overgrown.thaumaturge.block.vessel.AspectReactionPrimitiveManager;
34
import dev.overgrown.thaumaturge.networking.FocalManipulatorPackets;
45
import dev.overgrown.thaumaturge.networking.GauntletCastPackets;
56
import dev.overgrown.thaumaturge.recipe.VesselRecipe;
@@ -10,8 +11,10 @@
1011
import dev.overgrown.thaumaturge.spell.input.GauntletInputTracker;
1112
import dev.overgrown.thaumaturge.spell.input.GauntletInteractionBlocklist;
1213
import net.fabricmc.api.ModInitializer;
14+
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
1315
import net.minecraft.registry.Registries;
1416
import net.minecraft.registry.Registry;
17+
import net.minecraft.resource.ResourceType;
1518
import net.minecraft.util.Identifier;
1619
import org.slf4j.Logger;
1720
import org.slf4j.LoggerFactory;
@@ -84,6 +87,10 @@ public void onInitialize() {
8487
GauntletInteractionBlocklist.register(ModBlocks.ALCHEMICAL_FURNACE);
8588
GauntletInteractionBlocklist.register(ModBlocks.FOCAL_MANIPULATOR);
8689

90+
// Resource Manager
91+
ResourceManagerHelper server = ResourceManagerHelper.get(ResourceType.SERVER_DATA);
92+
server.registerReloadListener(new AspectReactionPrimitiveManager());
93+
8794
LOGGER.info("Thaumaturge initialized!");
8895
}
8996
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package dev.overgrown.thaumaturge.block.vessel;
2+
3+
import net.minecraft.util.Identifier;
4+
5+
import java.util.Collections;
6+
import java.util.Set;
7+
8+
/** AspectReaction
9+
* Object containing the input and output sets of Aspect for a Reaction
10+
*/
11+
public class AspectReaction {
12+
public Set<Identifier> input;
13+
public Set<Identifier> output;
14+
15+
public AspectReaction(Set<Identifier> input, Set<Identifier> output) {
16+
this.input = Collections.unmodifiableSet(input);
17+
this.output = Collections.unmodifiableSet(output);
18+
}
19+
}
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
package dev.overgrown.thaumaturge.block.vessel;
2+
3+
import dev.overgrown.thaumaturge.util.AspectMap;
4+
import net.minecraft.util.Identifier;
5+
import net.minecraft.util.math.random.Random;
6+
7+
import java.nio.ByteBuffer;
8+
import java.security.MessageDigest;
9+
import java.security.NoSuchAlgorithmException;
10+
import java.util.*;
11+
12+
public class AspectReactionHolder {
13+
14+
private static final int MIN_TEMP = -2;
15+
private static final int MAX_TEMP = 4;
16+
17+
private static final MessageDigest digest;
18+
19+
static {
20+
try {
21+
digest = MessageDigest.getInstance("SHA-256");
22+
} catch (NoSuchAlgorithmException e) {
23+
throw new RuntimeException("Thaumaturge: Could not Access Required SHA-256 Digest");
24+
}
25+
}
26+
27+
private Map<Integer,Map<Identifier, List<AspectReaction>>> reactionMap;
28+
29+
public AspectReactionHolder(long seed) {
30+
List<AspectReactionPrimitive> primitiveList = AspectReactionPrimitiveManager.getPrimitiveList();
31+
reactionMap = new HashMap<>();
32+
33+
for (AspectReactionPrimitive primitive: primitiveList) {
34+
35+
// Get a Random Object which Seeds depends upon world seed and primary aspects in a reaction
36+
byte[] inputA = primitive.inputA().toString().getBytes();
37+
byte[] inputB = primitive.inputB().toString().getBytes();
38+
byte[] result = primitive.result().toString().getBytes();
39+
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES + inputA.length + inputB.length + result.length);
40+
buffer.putLong(seed);
41+
buffer.put(inputA);
42+
buffer.put(inputB);
43+
buffer.put(result);
44+
digest.reset();
45+
byte[] hashResult = digest.digest(buffer.array());
46+
long randomSeed = ((long) hashResult[0]) << 56 |
47+
((long) hashResult[1]) << 48 |
48+
((long) hashResult[2]) << 40 |
49+
((long) hashResult[3]) << 32 |
50+
((long) hashResult[4]) << 24 |
51+
((long) hashResult[5]) << 16 |
52+
((long) hashResult[6]) << 8 |
53+
((long) hashResult[7]);
54+
Random random = Random.create(randomSeed);
55+
56+
int reactionTemp;
57+
boolean forwardReactionTrendsTowardsColder = primitive.frTowardsColder();
58+
59+
if (primitive.FrBegin() == primitive.FrEnd()) {
60+
reactionTemp = primitive.FrEnd();
61+
} else {
62+
reactionTemp = random.nextBetweenExclusive(primitive.FrBegin(), primitive.FrEnd());
63+
}
64+
65+
if (primitive.reactionDirectionReversible()) {
66+
forwardReactionTrendsTowardsColder = random.nextBoolean();
67+
}
68+
69+
Identifier catalyst = null;
70+
71+
if (!primitive.catalysts().isEmpty()) {
72+
if (random.nextFloat() <= primitive.catalystChance()) {
73+
catalyst = primitive.catalysts().get(random.nextInt(primitive.catalysts().size()));
74+
}
75+
}
76+
77+
AspectReaction forwardImplementation = new AspectReaction(
78+
Set.of(primitive.inputA(), primitive.inputB()), Set.of(primitive.result()));
79+
AspectReaction backwardImplementation = new AspectReaction(
80+
Set.of(primitive.result()), Set.of(primitive.inputA(), primitive.inputB()));
81+
82+
// Iterate over all temps form low to the mid
83+
for (int i = MIN_TEMP; i <= reactionTemp; i++) {
84+
// Access or create a Map of Reactions that can occur at a temperature
85+
Map<Identifier, List<AspectReaction>> reactionMapAtTemp;
86+
if (reactionMap.containsKey(i)) {
87+
reactionMapAtTemp = reactionMap.get(i);
88+
} else {
89+
reactionMapAtTemp = new HashMap<>();
90+
reactionMap.put(i, reactionMapAtTemp);
91+
}
92+
93+
Identifier key = catalyst == null ?
94+
(forwardReactionTrendsTowardsColder ? primitive.inputA() : primitive.result()) : catalyst;
95+
96+
// Access or Create a List of Reactions for a Aspect at a specific Temperature
97+
List<AspectReaction> aspectReactionsForAspectAtTemp;
98+
if (reactionMapAtTemp.containsKey(key)) {
99+
aspectReactionsForAspectAtTemp = reactionMapAtTemp.get(key);
100+
} else {
101+
aspectReactionsForAspectAtTemp = new ArrayList<>();
102+
reactionMapAtTemp.put(key, aspectReactionsForAspectAtTemp);
103+
}
104+
aspectReactionsForAspectAtTemp.add(forwardReactionTrendsTowardsColder ? forwardImplementation : backwardImplementation);
105+
}
106+
107+
// Iterate over all temps from high to end
108+
for (int i = reactionTemp + 1; i <= MAX_TEMP; i++) {
109+
// Access or create a Map of Reactions that can occur at a temperature
110+
Map<Identifier, List<AspectReaction>> reactionMapAtTemp;
111+
if (reactionMap.containsKey(i)) {
112+
reactionMapAtTemp = reactionMap.get(i);
113+
} else {
114+
reactionMapAtTemp = new HashMap<>();
115+
reactionMap.put(i, reactionMapAtTemp);
116+
}
117+
118+
Identifier key = catalyst == null ?
119+
(forwardReactionTrendsTowardsColder ? primitive.result() : primitive.inputA()) : catalyst;
120+
121+
// Access or Create a List of Reactions for a Aspect at a specific Temperature
122+
List<AspectReaction> aspectReactionsForAspectAtTemp;
123+
if (reactionMapAtTemp.containsKey(key)) {
124+
aspectReactionsForAspectAtTemp = reactionMapAtTemp.get(key);
125+
} else {
126+
aspectReactionsForAspectAtTemp = new ArrayList<>();
127+
reactionMapAtTemp.put(key, aspectReactionsForAspectAtTemp);
128+
}
129+
aspectReactionsForAspectAtTemp.add(forwardReactionTrendsTowardsColder ? backwardImplementation : forwardImplementation);
130+
}
131+
132+
133+
}
134+
}
135+
136+
public List<AspectReaction> getPossibleReactions(int temp, AspectMap map) {
137+
List<AspectReaction> list = new ArrayList<>();
138+
if (reactionMap.containsKey(temp)) {
139+
var mapAtTemp = reactionMap.get(temp);
140+
// Query valid Reaction Implementation for all Aspects
141+
for (Identifier aspect : map.getAspects()) {
142+
if (map.getAspectLevel(aspect) > 0 && mapAtTemp.containsKey(aspect)) {
143+
List<AspectReaction> listPerAspectAtTemp = mapAtTemp.get(aspect);
144+
for (AspectReaction implementation : listPerAspectAtTemp) {
145+
boolean containsAllInputs = true;
146+
for (Identifier inputAspect : implementation.input) {
147+
if (map.getAspectLevel(inputAspect) <= 0) {
148+
containsAllInputs = false;
149+
break;
150+
}
151+
}
152+
if (containsAllInputs) {
153+
list.add(implementation);
154+
}
155+
}
156+
}
157+
}
158+
}
159+
return list;
160+
}
161+
162+
@Override
163+
public String toString() {
164+
StringBuilder builder = new StringBuilder();
165+
builder.append("{\n");
166+
for (var temperatures : reactionMap.keySet()) {
167+
builder.append("\tTemp: ").append(temperatures).append(" {\n");
168+
var mapAtTemperature = reactionMap.get(temperatures);
169+
for (var aspect : mapAtTemperature.keySet()) {
170+
builder.append("\t\tAspect: ").append(aspect).append(" [\n");
171+
var reactionList = mapAtTemperature.get(aspect);
172+
for (var reaction : reactionList) {
173+
builder.append("\t\t\t{\n")
174+
.append("\t\t\t\t Input:").append(reaction.input).append("\n")
175+
.append("\t\t\t\t Output:").append(reaction.output).append("\n")
176+
.append("\t\t\t}\n");
177+
}
178+
builder.append("\t\t]");
179+
}
180+
builder.append("\t}\n");
181+
}
182+
183+
builder.append("}");
184+
return builder.toString();
185+
}
186+
187+
public interface Provider {
188+
AspectReactionHolder thaumaturge$getAspectReactionHolder();
189+
}
190+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package dev.overgrown.thaumaturge.block.vessel;
2+
3+
import com.mojang.serialization.Codec;
4+
import com.mojang.serialization.codecs.RecordCodecBuilder;
5+
import net.minecraft.util.Identifier;
6+
import org.jetbrains.annotations.NotNull;
7+
import java.util.List;
8+
9+
/***
10+
* AspectReactionPrimitive
11+
* Holds Information from which to generate per Seed unique Reactions between Aspects
12+
* @param inputA First Source Aspect
13+
* @param inputB Second Source Aspect
14+
* @param result Resulting Aspect
15+
* @param FrBegin Start of the Range at which the forward reaction starting point can be in
16+
* @param FrEnd End of the Range at which the forward reaction starting temperature point can be in
17+
* @param reactionDirectionReversible If the reaction direction implied between FrBegin and FrEnd can randomly be reversed
18+
* @param catalystChance Chance of the reaction needing to have a catalyst
19+
* @param catalysts List of possible catalyst for this reaction
20+
*/
21+
public record AspectReactionPrimitive(
22+
@NotNull
23+
Identifier inputA,
24+
@NotNull
25+
Identifier inputB,
26+
@NotNull
27+
Identifier result,
28+
int FrBegin,
29+
int FrEnd,
30+
boolean frTowardsColder,
31+
boolean reactionDirectionReversible,
32+
float catalystChance,
33+
@NotNull
34+
List<Identifier> catalysts) {
35+
36+
public AspectReactionPrimitive(
37+
@NotNull
38+
Identifier inputA,
39+
@NotNull
40+
Identifier inputB,
41+
@NotNull
42+
Identifier result,
43+
int FrBegin,
44+
int FrEnd,
45+
boolean frTowardsColder,
46+
boolean reactionDirectionReversible,
47+
float catalystChance,
48+
@NotNull
49+
List<Identifier> catalysts)
50+
{
51+
this.inputA = inputA;
52+
this.inputB = inputB;
53+
this.result = result;
54+
this.FrBegin = FrBegin;
55+
this.FrEnd = FrEnd;
56+
this.frTowardsColder = frTowardsColder;
57+
this.reactionDirectionReversible = reactionDirectionReversible;
58+
this.catalystChance = catalystChance;
59+
this.catalysts = catalysts;
60+
61+
if (this.inputA.equals(inputB)) {
62+
throw new RuntimeException("Inputs might not be of the same aspect");
63+
}
64+
65+
if (this.FrBegin > this.FrEnd) {
66+
throw new RuntimeException("Forward Direction Min can not be greater than Forward Direction Max");
67+
}
68+
}
69+
70+
public static final Codec<AspectReactionPrimitive> CODEC = RecordCodecBuilder.create(instance ->
71+
instance.group(
72+
Identifier.CODEC.fieldOf("input_a").forGetter(AspectReactionPrimitive::inputA),
73+
Identifier.CODEC.fieldOf("input_b").forGetter(AspectReactionPrimitive::inputB),
74+
Identifier.CODEC.fieldOf("result").forGetter(AspectReactionPrimitive::result),
75+
Codec.INT.fieldOf("fr_min").forGetter(AspectReactionPrimitive::FrBegin),
76+
Codec.INT.fieldOf("fr_max").forGetter(AspectReactionPrimitive::FrEnd),
77+
Codec.BOOL.fieldOf("fr_towards_colder").forGetter(AspectReactionPrimitive::frTowardsColder),
78+
Codec.BOOL.optionalFieldOf("fr_direction_reversible", false).forGetter(AspectReactionPrimitive::reactionDirectionReversible),
79+
Codec.FLOAT.optionalFieldOf("catalyst_chance", 0f).forGetter(AspectReactionPrimitive::catalystChance),
80+
Identifier.CODEC.listOf().optionalFieldOf("catalysts", List.of()).forGetter(AspectReactionPrimitive::catalysts)
81+
).apply(instance, AspectReactionPrimitive::new));
82+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package dev.overgrown.thaumaturge.block.vessel;
2+
3+
import com.google.gson.Gson;
4+
import com.google.gson.JsonElement;
5+
import com.mojang.serialization.JsonOps;
6+
import dev.overgrown.thaumaturge.Thaumaturge;
7+
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
8+
import net.minecraft.resource.JsonDataLoader;
9+
import net.minecraft.resource.ResourceManager;
10+
import net.minecraft.util.Identifier;
11+
import net.minecraft.util.profiler.Profiler;
12+
import java.util.ArrayList;
13+
import java.util.Collections;
14+
import java.util.List;
15+
import java.util.Map;
16+
17+
public class AspectReactionPrimitiveManager extends JsonDataLoader implements IdentifiableResourceReloadListener {
18+
19+
private static final Gson GSON = new Gson();
20+
private static final List<AspectReactionPrimitive> PRIMITIVE_LIST = new ArrayList<>();
21+
22+
public AspectReactionPrimitiveManager() {
23+
super(GSON, "aspect_reaction_primitive");
24+
}
25+
26+
@Override
27+
public Identifier getFabricId() {
28+
return Thaumaturge.identifier("aspect_reaction_primitive");
29+
}
30+
31+
@Override
32+
protected void apply(Map<Identifier, JsonElement> prepared, ResourceManager manager, Profiler profiler) {
33+
PRIMITIVE_LIST.clear();
34+
35+
for (Map.Entry<Identifier, JsonElement> entry : prepared.entrySet()) {
36+
Identifier id = entry.getKey();
37+
JsonElement json = entry.getValue();
38+
39+
AspectReactionPrimitive.CODEC.parse(JsonOps.INSTANCE, json)
40+
.resultOrPartial(error -> Thaumaturge.LOGGER.error("Failed to parse {}: {}", id, error))
41+
.ifPresent(PRIMITIVE_LIST::add);
42+
}
43+
44+
Thaumaturge.LOGGER.info("Loaded {} Aspect Reaction Primitives", PRIMITIVE_LIST.size());
45+
Thaumaturge.LOGGER.info(PRIMITIVE_LIST.toString());
46+
}
47+
48+
public static List<AspectReactionPrimitive> getPrimitiveList() {
49+
return Collections.unmodifiableList(PRIMITIVE_LIST);
50+
}
51+
}

0 commit comments

Comments
 (0)