Skip to content

Commit 98ecbcc

Browse files
authored
Serialize AdjacentFluidCondition to string (#3830)
1 parent be4be08 commit 98ecbcc

File tree

3 files changed

+169
-94
lines changed

3 files changed

+169
-94
lines changed
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
package com.gregtechceu.gtceu.api.recipe.condition;
2+
3+
import com.gregtechceu.gtceu.api.registry.GTRegistries;
4+
5+
import net.minecraft.core.Holder;
6+
import net.minecraft.core.HolderSet;
7+
import net.minecraft.core.registries.BuiltInRegistries;
8+
import net.minecraft.core.registries.Registries;
9+
import net.minecraft.resources.ResourceKey;
10+
import net.minecraft.resources.ResourceLocation;
11+
import net.minecraft.tags.TagKey;
12+
import net.minecraft.world.level.material.Fluid;
13+
import net.minecraft.world.level.material.Fluids;
14+
15+
import java.util.ArrayList;
16+
import java.util.List;
17+
import java.util.Optional;
18+
import java.util.stream.Collectors;
19+
20+
public class ConditionSerializeUtils {
21+
22+
/**
23+
* Encode a list of HolderSet<Fluids> to a string encoding
24+
*
25+
* @param fluids the List<HolderSet<Fluids>> to be encoded
26+
* @return A string, where HolderSets are separated by |'s and elements of the HolderSet are separated by ,
27+
*/
28+
public static String encodeFluids(List<HolderSet<Fluid>> fluids) {
29+
StringBuilder sb = new StringBuilder();
30+
boolean first = true;
31+
32+
for (HolderSet<Fluid> holderSet : fluids) {
33+
if (!first) {
34+
sb.append("|");
35+
}
36+
sb.append(encodeHolderSet(holderSet));
37+
first = false;
38+
}
39+
40+
return sb.toString();
41+
}
42+
43+
/**
44+
* Encode a HolderSet<Fluids> to a string encoding
45+
* The encoding is a string, where if it's a tag, it's encoded as #+location,
46+
* and if it's a list, elements of the HolderSet are separated by ,
47+
*
48+
* @param holderSet the HolderSet<Fluids> to be encoded
49+
* @return the encoded string
50+
*/
51+
public static String encodeHolderSet(HolderSet<Fluid> holderSet) {
52+
return holderSet.unwrap().map(
53+
// Case 1: Tag
54+
tagKey -> "#" + tagKey.location(),
55+
// Case 2: Direct list of holders
56+
holders -> holders.stream()
57+
.map(holder -> getStringFromHolder(holder))
58+
.collect(Collectors.joining(",")));
59+
}
60+
61+
/**
62+
* Encode a Holder<Fluid> into a String.
63+
*
64+
* @param holder the Holder<Fluid> to be encoded
65+
* @return a string encoding, as # + location if it's a tagkey, or the location if it's a registry entry.
66+
*/
67+
public static String getStringFromHolder(Holder<Fluid> holder) {
68+
// Case 1: If the holder has a registry key, use it
69+
Optional<ResourceKey<Fluid>> keyOpt = holder.unwrapKey();
70+
if (keyOpt.isPresent()) {
71+
return keyOpt.get().location().toString();
72+
}
73+
74+
// Case 2: If the holder is tagged, return the first tag with a '#' prefix
75+
Optional<TagKey<Fluid>> tagOpt = holder.tags().findFirst();
76+
if (tagOpt.isPresent()) {
77+
return "#" + tagOpt.get().location().toString();
78+
}
79+
80+
throw new RuntimeException("could not deserialize holder: " + holder);
81+
}
82+
83+
/**
84+
* Decode a FluidString into a List<HolderSet<Fluid>>
85+
* The encoding is a string, where if it's a tag, it's encoded as #+location,
86+
* and if it's a list, elements of the HolderSet are separated by ,
87+
*
88+
* @param fluidString the string encoding
89+
* @return The decoded list
90+
*/
91+
public static List<HolderSet<Fluid>> decodeFluids(String fluidString) {
92+
List<HolderSet<Fluid>> result = new ArrayList<>();
93+
for (String token : fluidString.split("\\|")) {
94+
if (!token.isBlank()) {
95+
result.add(decodeHolderSet(token));
96+
}
97+
}
98+
return result;
99+
}
100+
101+
/**
102+
* Decode a string into a HolderSet<Fluid>
103+
* The encoding is a string, where if it's a tag, it's encoded as #+location,
104+
* and if it's a list, elements of the HolderSet are separated by ,
105+
*
106+
* @param encodedSet the encoded set
107+
* @return The decoded list
108+
*/
109+
public static HolderSet<Fluid> decodeHolderSet(String encodedSet) {
110+
encodedSet = encodedSet.trim();
111+
if (encodedSet.isEmpty()) {
112+
return HolderSet.direct(List.of());
113+
}
114+
115+
// Case 1: Tag-based holder set
116+
if (encodedSet.startsWith("#")) {
117+
ResourceLocation tagId = new ResourceLocation(encodedSet.substring(1));
118+
TagKey<Fluid> tagKey = TagKey.create(Registries.FLUID, tagId);
119+
return GTRegistries.builtinRegistry().registry(Registries.FLUID).get().getOrCreateTag(tagKey);
120+
}
121+
122+
// Case 2: Direct list of fluids
123+
String[] parts = encodedSet.split(",");
124+
List<Holder<Fluid>> holders = new ArrayList<>();
125+
for (String part : parts) {
126+
ResourceLocation rl = new ResourceLocation(part.trim());
127+
Fluid fluid = GTRegistries.builtinRegistry().registry(Registries.FLUID).get().get(rl);
128+
if (fluid != null && fluid != Fluids.EMPTY) {
129+
holders.add(BuiltInRegistries.FLUID.wrapAsHolder(fluid));
130+
} else {
131+
throw new RuntimeException("Unknown fluid id: " + rl);
132+
}
133+
}
134+
return HolderSet.direct(holders);
135+
}
136+
}

src/main/java/com/gregtechceu/gtceu/common/recipe/condition/AdjacentFluidCondition.java

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@
99

1010
import net.minecraft.core.BlockPos;
1111
import net.minecraft.core.HolderSet;
12-
import net.minecraft.core.RegistryCodecs;
1312
import net.minecraft.core.registries.BuiltInRegistries;
14-
import net.minecraft.core.registries.Registries;
1513
import net.minecraft.network.chat.Component;
1614
import net.minecraft.resources.ResourceLocation;
1715
import net.minecraft.tags.TagKey;
18-
import net.minecraft.util.ExtraCodecs;
1916
import net.minecraft.world.level.Level;
2017
import net.minecraft.world.level.material.Fluid;
2118
import net.minecraft.world.level.material.FluidState;
@@ -24,35 +21,53 @@
2421
import com.mojang.serialization.codecs.RecordCodecBuilder;
2522
import lombok.Getter;
2623
import lombok.NoArgsConstructor;
27-
import lombok.Setter;
2824
import org.jetbrains.annotations.NotNull;
25+
import org.jetbrains.annotations.Nullable;
2926

3027
import java.util.*;
3128

29+
import static com.gregtechceu.gtceu.api.recipe.condition.ConditionSerializeUtils.decodeFluids;
30+
import static com.gregtechceu.gtceu.api.recipe.condition.ConditionSerializeUtils.encodeFluids;
31+
3232
@NoArgsConstructor
3333
public class AdjacentFluidCondition extends RecipeCondition {
3434

3535
// spotless:off
36-
public static final Codec<List<HolderSet<Fluid>>> FLUID_CODEC = ExtraCodecs.lazyInitializedCodec(
37-
() -> RegistryCodecs.homogeneousList(Registries.FLUID).listOf()
38-
);
39-
40-
public static final Codec<AdjacentFluidCondition> CODEC = RecordCodecBuilder.create(instance -> RecipeCondition.isReverse(instance).and(
41-
FLUID_CODEC.fieldOf("fluids").forGetter(AdjacentFluidCondition::getFluids)
42-
).apply(instance, AdjacentFluidCondition::new));
36+
public static final Codec<AdjacentFluidCondition> CODEC =
37+
RecordCodecBuilder.create(instance -> RecipeCondition.isReverse(instance).and(
38+
Codec.STRING.fieldOf("fluidString").forGetter(AdjacentFluidCondition::getFluidString)
39+
).apply(instance, AdjacentFluidCondition::new));
4340
// spotless:on
4441

4542
@Getter
46-
@Setter
47-
private @NotNull List<HolderSet<Fluid>> fluids = new ArrayList<>();
43+
private @NotNull String fluidString = "";
44+
45+
private @Nullable List<HolderSet<Fluid>> fluids = null;
46+
47+
public void setFluids(@NotNull List<HolderSet<Fluid>> fluids) {
48+
this.fluids = fluids;
49+
this.fluidString = encodeFluids(fluids);
50+
}
51+
52+
public List<HolderSet<Fluid>> getFluids() {
53+
if (fluids == null) {
54+
fluids = decodeFluids(getFluidString());
55+
}
56+
return fluids;
57+
}
4858

4959
public AdjacentFluidCondition(@NotNull List<HolderSet<Fluid>> fluids) {
50-
this.fluids.addAll(fluids);
60+
this.setFluids(fluids);
61+
}
62+
63+
public AdjacentFluidCondition(boolean isReverse, String fluidString) {
64+
super(isReverse);
65+
this.fluidString = fluidString;
5166
}
5267

5368
public AdjacentFluidCondition(boolean isReverse, @NotNull List<HolderSet<Fluid>> fluids) {
5469
super(isReverse);
55-
this.fluids.addAll(fluids);
70+
this.setFluids(fluids);
5671
}
5772

5873
public static AdjacentFluidCondition fromFluids(Collection<Fluid> fluids) {
@@ -114,7 +129,7 @@ public boolean testCondition(@NotNull GTRecipe recipe, @NotNull RecipeLogic reci
114129
}
115130

116131
public @NotNull List<HolderSet<Fluid>> getOrInitFluids(@NotNull GTRecipe recipe) {
117-
if (this.fluids.isEmpty() || (recipe.data.contains("fluidA") && recipe.data.contains("fluidB"))) {
132+
if (this.getFluids().isEmpty() || (recipe.data.contains("fluidA") && recipe.data.contains("fluidB"))) {
118133
List<HolderSet<Fluid>> fluids = new ArrayList<>();
119134

120135
Fluid fluidA = BuiltInRegistries.FLUID.get(new ResourceLocation(recipe.data.getString("fluidA")));
@@ -126,9 +141,9 @@ public boolean testCondition(@NotNull GTRecipe recipe, @NotNull RecipeLogic reci
126141
fluids.add(HolderSet.direct(fluidB.builtInRegistryHolder()));
127142
}
128143

129-
this.fluids = fluids;
144+
this.setFluids(fluids);
130145
}
131-
return this.fluids;
146+
return this.getFluids();
132147
}
133148

134149
@Override

src/test/java/com/gregtechceu/gtceu/api/recipe/GTRecipeSerializerTest.java

Lines changed: 0 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -100,82 +100,6 @@ public static void testAdjacentFluidConditionRoundTrip(GameTestHelper helper) {
100100
helper.succeed();
101101
}
102102

103-
@GameTest(template = "empty_5x5")
104-
public static void testFluidCodecDirect(GameTestHelper helper) {
105-
var ops = RegistryOps.create(JsonOps.INSTANCE, GTRegistries.builtinRegistry());
106-
107-
// Direct fluid: water
108-
HolderSet<Fluid> waterSet = HolderSet.direct(Fluids.WATER.builtInRegistryHolder());
109-
List<HolderSet<Fluid>> list = List.of(waterSet);
110-
111-
// Serialize
112-
JsonElement json = AdjacentFluidCondition.FLUID_CODEC.encodeStart(ops, list)
113-
.getOrThrow(false, System.err::println);
114-
115-
// Deserialize
116-
List<HolderSet<Fluid>> decoded = AdjacentFluidCondition.FLUID_CODEC.parse(ops, json)
117-
.getOrThrow(false, System.err::println);
118-
119-
helper.assertTrue(decoded.size() == 1, "Expected 1 fluid set");
120-
helper.assertTrue(decoded.get(0).contains(Fluids.WATER.builtInRegistryHolder()), "Should contain water");
121-
helper.succeed();
122-
}
123-
124-
@GameTest(template = "empty_5x5")
125-
public static void testFluidCodecTag(GameTestHelper helper) {
126-
var ops = RegistryOps.create(JsonOps.INSTANCE, GTRegistries.builtinRegistry());
127-
128-
// Tag: forge:lava
129-
TagKey<Fluid> lavaTag = TagKey.create(Registries.FLUID, new ResourceLocation("forge", "lava"));
130-
HolderSet<Fluid> lavaSet = GTRegistries.builtinRegistry()
131-
.registryOrThrow(Registries.FLUID)
132-
.getOrCreateTag(lavaTag);
133-
List<HolderSet<Fluid>> list = List.of(lavaSet);
134-
135-
// Serialize
136-
JsonElement json = AdjacentFluidCondition.FLUID_CODEC.encodeStart(ops, list)
137-
.getOrThrow(false, System.err::println);
138-
139-
// Deserialize
140-
List<HolderSet<Fluid>> decoded = AdjacentFluidCondition.FLUID_CODEC.parse(ops, json)
141-
.getOrThrow(false, System.err::println);
142-
143-
helper.assertTrue(decoded.size() == 1, "Expected 1 fluid set");
144-
helper.assertTrue(decoded.get(0).unwrapKey().isPresent() && decoded.get(0).unwrapKey().get().equals(lavaTag),
145-
"Should be forge:lava tag");
146-
helper.succeed();
147-
}
148-
149-
@GameTest(template = "empty_5x5")
150-
public static void testFluidCodecMixed(GameTestHelper helper) {
151-
var ops = RegistryOps.create(JsonOps.INSTANCE, GTRegistries.builtinRegistry());
152-
153-
// Direct: water
154-
HolderSet<Fluid> waterSet = HolderSet.direct(Fluids.WATER.builtInRegistryHolder());
155-
156-
// Tag: forge:lava
157-
TagKey<Fluid> lavaTag = TagKey.create(Registries.FLUID, new ResourceLocation("forge", "lava"));
158-
HolderSet<Fluid> lavaSet = GTRegistries.builtinRegistry()
159-
.registryOrThrow(Registries.FLUID)
160-
.getOrCreateTag(lavaTag);
161-
162-
List<HolderSet<Fluid>> list = List.of(waterSet, lavaSet);
163-
164-
// Serialize
165-
JsonElement json = AdjacentFluidCondition.FLUID_CODEC.encodeStart(ops, list)
166-
.getOrThrow(false, System.err::println);
167-
168-
// Deserialize
169-
List<HolderSet<Fluid>> decoded = AdjacentFluidCondition.FLUID_CODEC.parse(ops, json)
170-
.getOrThrow(false, System.err::println);
171-
172-
helper.assertTrue(decoded.size() == 2, "Expected 2 fluid sets");
173-
helper.assertTrue(decoded.get(0).contains(Fluids.WATER.builtInRegistryHolder()), "First should be water");
174-
helper.assertTrue(decoded.get(1).unwrapKey().isPresent() && decoded.get(1).unwrapKey().get().equals(lavaTag),
175-
"Second should be forge:lava tag");
176-
helper.succeed();
177-
}
178-
179103
@GameTest(template = "empty_5x5")
180104
public static void testConditionSerializeThenCodecDeserialize(GameTestHelper helper) {
181105
var ops = RegistryOps.create(JsonOps.INSTANCE, GTRegistries.builtinRegistry());

0 commit comments

Comments
 (0)