Fix recipe syncing, improve (add) error handling.

This commit is contained in:
modmuss50 2022-01-27 20:34:26 +00:00
parent bd6bcf55ab
commit fa657d46e8
24 changed files with 273 additions and 126 deletions

View file

@ -32,6 +32,7 @@ import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper; import net.minecraft.util.JsonHelper;
import net.minecraft.world.World; import net.minecraft.world.World;
import reborncore.common.crafting.serde.RecipeSerde; import reborncore.common.crafting.serde.RecipeSerde;
import reborncore.common.crafting.serde.RecipeSerdeException;
import reborncore.common.util.serialization.SerializationUtil; import reborncore.common.util.serialization.SerializationUtil;
import java.util.List; import java.util.List;
@ -47,14 +48,22 @@ public record RebornRecipeType<R extends RebornRecipe>(
throw new RuntimeException("RebornRecipe type not supported!"); throw new RuntimeException("RebornRecipe type not supported!");
} }
try {
return recipeSerde.fromJson(json, this, recipeId); return recipeSerde.fromJson(json, this, recipeId);
} catch (Throwable e) {
throw new RecipeSerdeException(recipeId, e);
}
} }
private JsonObject toJson(R recipe) { public JsonObject toJson(R recipe, boolean networkSync) {
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("type", name.toString()); jsonObject.addProperty("type", name.toString());
recipeSerde.toJson(recipe, jsonObject); try {
recipeSerde.toJson(recipe, jsonObject, networkSync);
} catch (Throwable e) {
throw new RecipeSerdeException(recipe.getId(), e);
}
return jsonObject; return jsonObject;
} }
@ -67,7 +76,7 @@ public record RebornRecipeType<R extends RebornRecipe>(
@Override @Override
public void write(PacketByteBuf buffer, R recipe) { public void write(PacketByteBuf buffer, R recipe) {
String output = SerializationUtil.GSON_FLAT.toJson(toJson(recipe)); String output = SerializationUtil.GSON_FLAT.toJson(toJson(recipe, true));
buffer.writeInt(output.length()); buffer.writeInt(output.length());
buffer.writeString(output); buffer.writeString(output);
} }

View file

@ -62,7 +62,7 @@ public class RecipeManager {
return recipeTypes.get(name); return recipeTypes.get(name);
} }
public static List<RebornRecipeType> getRecipeTypes(String namespace) { public static List<RebornRecipeType<?>> getRecipeTypes(String namespace) {
return recipeTypes.values().stream().filter(rebornRecipeType -> rebornRecipeType.name().getNamespace().equals(namespace)).collect(Collectors.toList()); return recipeTypes.values().stream().filter(rebornRecipeType -> rebornRecipeType.name().getNamespace().equals(namespace)).collect(Collectors.toList());
} }
} }

View file

@ -45,16 +45,15 @@ import reborncore.common.util.serialization.SerializationUtil;
import reborncore.mixin.common.AccessorRecipeManager; import reborncore.mixin.common.AccessorRecipeManager;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
public class RecipeUtils { public class RecipeUtils {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends RebornRecipe> List<T> getRecipes(World world, RebornRecipeType<T> type) { public static <T extends RebornRecipe> List<T> getRecipes(World world, RebornRecipeType<T> type) {
final AccessorRecipeManager accessorRecipeManager = (AccessorRecipeManager) world.getRecipeManager(); final AccessorRecipeManager accessorRecipeManager = (AccessorRecipeManager) world.getRecipeManager();
final Collection<Recipe<Inventory>> recipes = accessorRecipeManager.getAll(type).values(); final Collection<Recipe<Inventory>> recipes = accessorRecipeManager.getAll(type).values().stream().toList();
//noinspection unchecked //noinspection unchecked
return Collections.unmodifiableList((List<T>) (Object) recipes); return (List<T>) (Object) recipes;
} }
public static DefaultedList<ItemStack> deserializeItems(JsonElement jsonObject) { public static DefaultedList<ItemStack> deserializeItems(JsonElement jsonObject) {

View file

@ -0,0 +1,72 @@
/*
* This file is part of RebornCore, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 TeamReborn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package reborncore.common.crafting;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import net.minecraft.item.Item;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.ShapedRecipe;
import net.minecraft.tag.Tag;
import net.minecraft.util.JsonHelper;
import net.minecraft.util.registry.Registry;
import java.util.Map;
public class ShapedRecipeHelper {
public static JsonObject rewriteForNetworkSync(JsonObject shapedRecipeJson) {
final Map<String, Ingredient> symbols = ShapedRecipe.readSymbols(JsonHelper.getObject(shapedRecipeJson, "key"));
final JsonObject keys = new JsonObject();
for (Map.Entry<String, Ingredient> entry : symbols.entrySet()) {
if (entry.getKey().equals(" ")) continue;
JsonArray entries = new JsonArray();
for (Ingredient.Entry ingredientEntry : entry.getValue().entries) {
if (ingredientEntry instanceof Ingredient.StackEntry stackEntry) {
entries.add(stackEntry.toJson());
} else if (ingredientEntry instanceof Ingredient.TagEntry tagEntry) {
final Tag<Item> tag = tagEntry.tag;
final Item[] items = tag.values().toArray(new Item[0]);
for (Item item : items) {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("item", Registry.ITEM.getId(item).toString());
entries.add(jsonObject);
}
} else {
throw new UnsupportedOperationException("Cannot write " + ingredientEntry.getClass());
}
}
keys.add(entry.getKey(), entries);
}
shapedRecipeJson.remove("key");
shapedRecipeJson.add("key", keys);
return shapedRecipeJson;
}
}

View file

@ -54,7 +54,7 @@ public class DummyIngredient extends RebornIngredient {
} }
@Override @Override
protected JsonObject toJson() { protected JsonObject toJson(boolean networkSync) {
return new JsonObject(); return new JsonObject();
} }

View file

@ -137,7 +137,7 @@ public class FluidIngredient extends RebornIngredient {
} }
@Override @Override
public JsonObject toJson() { public JsonObject toJson(boolean networkSync) {
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("fluid", Registry.FLUID.getId(fluid).toString()); jsonObject.addProperty("fluid", Registry.FLUID.getId(fluid).toString());
if (holders.isPresent()) { if (holders.isPresent()) {

View file

@ -39,7 +39,6 @@ public class IngredientManager {
public static final Identifier STACK_RECIPE_TYPE = new Identifier("reborncore", "stack"); public static final Identifier STACK_RECIPE_TYPE = new Identifier("reborncore", "stack");
public static final Identifier FLUID_RECIPE_TYPE = new Identifier("reborncore", "fluid"); public static final Identifier FLUID_RECIPE_TYPE = new Identifier("reborncore", "fluid");
public static final Identifier TAG_RECIPE_TYPE = new Identifier("reborncore", "tag"); public static final Identifier TAG_RECIPE_TYPE = new Identifier("reborncore", "tag");
public static final Identifier WRAPPED_RECIPE_TYPE = new Identifier("reborncore", "wrapped");
private static final HashMap<Identifier, Function<JsonObject, RebornIngredient>> recipeTypes = new HashMap<>(); private static final HashMap<Identifier, Function<JsonObject, RebornIngredient>> recipeTypes = new HashMap<>();
@ -47,7 +46,6 @@ public class IngredientManager {
recipeTypes.put(STACK_RECIPE_TYPE, StackIngredient::deserialize); recipeTypes.put(STACK_RECIPE_TYPE, StackIngredient::deserialize);
recipeTypes.put(FLUID_RECIPE_TYPE, FluidIngredient::deserialize); recipeTypes.put(FLUID_RECIPE_TYPE, FluidIngredient::deserialize);
recipeTypes.put(TAG_RECIPE_TYPE, TagIngredient::deserialize); recipeTypes.put(TAG_RECIPE_TYPE, TagIngredient::deserialize);
recipeTypes.put(WRAPPED_RECIPE_TYPE, WrappedIngredient::deserialize);
} }
public static RebornIngredient deserialize(@Nullable JsonElement jsonElement) { public static RebornIngredient deserialize(@Nullable JsonElement jsonElement) {
@ -63,8 +61,6 @@ public class IngredientManager {
recipeTypeIdent = FLUID_RECIPE_TYPE; recipeTypeIdent = FLUID_RECIPE_TYPE;
} else if (json.has("tag")) { } else if (json.has("tag")) {
recipeTypeIdent = TAG_RECIPE_TYPE; recipeTypeIdent = TAG_RECIPE_TYPE;
} else if (json.has("wrapped")) {
recipeTypeIdent = WRAPPED_RECIPE_TYPE;
} }
if (json.has("type")) { if (json.has("type")) {
@ -77,5 +73,4 @@ public class IngredientManager {
} }
return recipeTypeFunction.apply(json); return recipeTypeFunction.apply(json);
} }
} }

View file

@ -48,17 +48,21 @@ public abstract class RebornIngredient implements Predicate<ItemStack> {
public abstract List<ItemStack> getPreviewStacks(); public abstract List<ItemStack> getPreviewStacks();
protected abstract JsonObject toJson(); protected abstract JsonObject toJson(boolean networkSync);
public abstract int getCount(); public abstract int getCount();
//Same as above but adds the type //Same as above but adds the type
public final JsonObject witeToJson() { public final JsonObject writeToJson(boolean networkSync) {
JsonObject jsonObject = toJson(); JsonObject jsonObject = toJson(networkSync);
jsonObject.addProperty("type", ingredientType.toString()); jsonObject.addProperty("type", ingredientType.toString());
return jsonObject; return jsonObject;
} }
public final JsonObject writeToSyncJson() {
return writeToJson(true);
}
public <T extends RebornIngredient> void ifType(Class<T> clazz, Consumer<T> consumer) { public <T extends RebornIngredient> void ifType(Class<T> clazz, Consumer<T> consumer) {
if (this.getClass().isAssignableFrom(clazz)) { if (this.getClass().isAssignableFrom(clazz)) {
//noinspection unchecked //noinspection unchecked

View file

@ -138,7 +138,7 @@ public class StackIngredient extends RebornIngredient {
} }
@Override @Override
public JsonObject toJson() { public JsonObject toJson(boolean networkSync) {
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("item", Registry.ITEM.getId(stacks.get(0).getItem()).toString()); jsonObject.addProperty("item", Registry.ITEM.getId(stacks.get(0).getItem()).toString());

View file

@ -104,7 +104,15 @@ public class TagIngredient extends RebornIngredient {
} }
@Override @Override
public JsonObject toJson() { public JsonObject toJson(boolean networkSync) {
if (networkSync) {
return toItemJsonObject();
}
throw new UnsupportedOperationException("TODO");
}
private JsonObject toItemJsonObject() {
//Tags are not synced across the server so we sync all the items //Tags are not synced across the server so we sync all the items
JsonObject jsonObject = new JsonObject(); JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("server_sync", true); jsonObject.addProperty("server_sync", true);

View file

@ -1,86 +0,0 @@
/*
* This file is part of RebornCore, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 TeamReborn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package reborncore.common.crafting.ingredient;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.Ingredient;
import java.util.Arrays;
import java.util.List;
public class WrappedIngredient extends RebornIngredient {
private Ingredient wrapped;
public WrappedIngredient() {
super(IngredientManager.WRAPPED_RECIPE_TYPE);
}
public WrappedIngredient(Ingredient wrapped) {
this();
this.wrapped = wrapped;
}
@Override
public boolean test(ItemStack itemStack) {
return wrapped.test(itemStack);
}
@Override
public Ingredient getPreview() {
return wrapped;
}
@Override
public List<ItemStack> getPreviewStacks() {
return Arrays.asList(wrapped.getMatchingStacks());
}
@Override
protected JsonObject toJson() {
if (wrapped.toJson() instanceof JsonObject) {
return (JsonObject) wrapped.toJson();
}
JsonObject jsonObject = new JsonObject();
jsonObject.add("options", wrapped.toJson());
return jsonObject;
}
@Override
public int getCount() {
return wrapped.getMatchingStacks().length;
}
public static RebornIngredient deserialize(JsonObject jsonObject) {
Ingredient underlying;
if (jsonObject.has("options") && jsonObject.get("options") instanceof JsonArray) {
underlying = Ingredient.fromJson(jsonObject.get("options"));
} else {
underlying = Ingredient.fromJson(jsonObject);
}
return new WrappedIngredient(underlying);
}
}

View file

@ -61,9 +61,9 @@ public abstract class AbstractRecipeSerde<R extends RebornRecipe> implements Rec
return RecipeUtils.deserializeItems(resultsJson); return RecipeUtils.deserializeItems(resultsJson);
} }
protected void writeIngredients(R recipe, JsonObject jsonObject) { protected void writeIngredients(R recipe, JsonObject jsonObject, boolean networkSync) {
final JsonArray ingredientsArray = new JsonArray(); final JsonArray ingredientsArray = new JsonArray();
recipe.getRebornIngredients().stream().map(RebornIngredient::witeToJson).forEach(ingredientsArray::add); recipe.getRebornIngredients().stream().map(RebornIngredient::writeToSyncJson).forEach(ingredientsArray::add);
jsonObject.add("ingredients", ingredientsArray); jsonObject.add("ingredients", ingredientsArray);
} }

View file

@ -49,7 +49,7 @@ public abstract class RebornFluidRecipeSerde<R extends RebornFluidRecipe> extend
final Fluid fluid = Registry.FLUID.get(identifier); final Fluid fluid = Registry.FLUID.get(identifier);
FluidValue value = FluidValue.BUCKET; FluidValue value = FluidValue.BUCKET;
if(tank.has("amount")){ if (tank.has("amount")){
value = FluidValue.parseFluidValue(tank.get("amount")); value = FluidValue.parseFluidValue(tank.get("amount"));
} }
@ -59,10 +59,13 @@ public abstract class RebornFluidRecipeSerde<R extends RebornFluidRecipe> extend
} }
@Override @Override
public void collectJsonData(R recipe, JsonObject jsonObject) { public void collectJsonData(R recipe, JsonObject jsonObject, boolean networkSync) {
final JsonObject tankObject = new JsonObject(); final JsonObject tankObject = new JsonObject();
tankObject.addProperty("fluid", Registry.FLUID.getId(recipe.getFluidInstance().getFluid()).toString()); tankObject.addProperty("fluid", Registry.FLUID.getId(recipe.getFluidInstance().getFluid()).toString());
tankObject.addProperty("value", recipe.getFluidInstance().getAmount().getRawValue());
var amountObject = new JsonObject();
amountObject.addProperty("droplets", recipe.getFluidInstance().getAmount().getRawValue());
tankObject.add("amount", amountObject);
jsonObject.add("tank", tankObject); jsonObject.add("tank", tankObject);
} }

View file

@ -48,16 +48,16 @@ public abstract class RebornRecipeSerde<R extends RebornRecipe> extends Abstract
return fromJson(jsonObject, type, name, ingredients, outputs, power, time); return fromJson(jsonObject, type, name, ingredients, outputs, power, time);
} }
protected abstract void collectJsonData(R recipe, JsonObject jsonObject); protected abstract void collectJsonData(R recipe, JsonObject jsonObject, boolean networkSync);
@Override @Override
public final void toJson(R recipe, JsonObject jsonObject) { public final void toJson(R recipe, JsonObject jsonObject, boolean networkSync) {
writePower(recipe, jsonObject); writePower(recipe, jsonObject);
writeTime(recipe, jsonObject); writeTime(recipe, jsonObject);
writeIngredients(recipe, jsonObject); writeIngredients(recipe, jsonObject, true);
writeOutputs(recipe, jsonObject); writeOutputs(recipe, jsonObject);
collectJsonData(recipe, jsonObject); collectJsonData(recipe, jsonObject, networkSync);
} }
public static <R extends RebornRecipe> RebornRecipeSerde<R> create(SimpleRecipeFactory<R> factory) { public static <R extends RebornRecipe> RebornRecipeSerde<R> create(SimpleRecipeFactory<R> factory) {
@ -68,7 +68,7 @@ public abstract class RebornRecipeSerde<R extends RebornRecipe> extends Abstract
} }
@Override @Override
protected void collectJsonData(R recipe, JsonObject jsonObject) { protected void collectJsonData(R recipe, JsonObject jsonObject, boolean networkSync) {
} }
}; };
} }

View file

@ -32,5 +32,5 @@ import reborncore.common.crafting.RebornRecipeType;
public interface RecipeSerde<R extends RebornRecipe> { public interface RecipeSerde<R extends RebornRecipe> {
R fromJson(JsonObject jsonObject, RebornRecipeType<R> type, Identifier name); R fromJson(JsonObject jsonObject, RebornRecipeType<R> type, Identifier name);
void toJson(R recipe, JsonObject jsonObject); void toJson(R recipe, JsonObject jsonObject, boolean networkSync);
} }

View file

@ -0,0 +1,39 @@
/*
* This file is part of RebornCore, licensed under the MIT License (MIT).
*
* Copyright (c) 2021 TeamReborn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package reborncore.common.crafting.serde;
import net.minecraft.util.Identifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RecipeSerdeException extends RuntimeException {
private static final Logger LOGGER = LoggerFactory.getLogger(RecipeSerdeException.class);
public RecipeSerdeException(Identifier identifier, Throwable cause) {
super("Failed to ser/de " + identifier, cause);
// Dont trust minecraft to log this.
LOGGER.error(getMessage(), this);
}
}

View file

@ -4,6 +4,12 @@ accessible method net/minecraft/recipe/ShapedRecipe readSymbols (Lco
accessible method net/minecraft/recipe/ShapedRecipe getPattern (Lcom/google/gson/JsonArray;)[Ljava/lang/String; accessible method net/minecraft/recipe/ShapedRecipe getPattern (Lcom/google/gson/JsonArray;)[Ljava/lang/String;
accessible method net/minecraft/recipe/ShapedRecipe createPatternMatrix ([Ljava/lang/String;Ljava/util/Map;II)Lnet/minecraft/util/collection/DefaultedList; accessible method net/minecraft/recipe/ShapedRecipe createPatternMatrix ([Ljava/lang/String;Ljava/util/Map;II)Lnet/minecraft/util/collection/DefaultedList;
accessible class net/minecraft/recipe/Ingredient$Entry
accessible class net/minecraft/recipe/Ingredient$TagEntry
accessible class net/minecraft/recipe/Ingredient$StackEntry
accessible field net/minecraft/recipe/Ingredient entries [Lnet/minecraft/recipe/Ingredient$Entry;
accessible field net/minecraft/recipe/Ingredient$TagEntry tag Lnet/minecraft/tag/Tag;
accessible method net/minecraft/client/gui/screen/ingame/HandledScreen getSlotAt (DD)Lnet/minecraft/screen/slot/Slot; accessible method net/minecraft/client/gui/screen/ingame/HandledScreen getSlotAt (DD)Lnet/minecraft/screen/slot/Slot;
accessible method net/minecraft/client/render/WorldRenderer drawShapeOutline (Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;Lnet/minecraft/util/shape/VoxelShape;DDDFFFF)V accessible method net/minecraft/client/render/WorldRenderer drawShapeOutline (Lnet/minecraft/client/util/math/MatrixStack;Lnet/minecraft/client/render/VertexConsumer;Lnet/minecraft/util/shape/VoxelShape;DDDFFFF)V

View file

@ -78,6 +78,10 @@ allprojects {
sourceCompatibility = JavaVersion.VERSION_17 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17
loom {
runtimeOnlyLog4j = true
}
// Shared deps between TR and RC // Shared deps between TR and RC
dependencies { dependencies {
minecraft "com.mojang:minecraft:${project.minecraft_version}" minecraft "com.mojang:minecraft:${project.minecraft_version}"
@ -173,8 +177,12 @@ dependencies {
disabledOptionalDependency "net.oskarstrom:DashLoader:${project.dashloader_version}" disabledOptionalDependency "net.oskarstrom:DashLoader:${project.dashloader_version}"
// Use groovy for datagen/gametest, if you are copying this you prob dont want it. // Use groovy for datagen/gametest, if you are copying this you prob dont want it.
gametestImplementation 'org.apache.groovy:groovy:4.0.0-beta-2' gametestImplementation 'org.apache.groovy:groovy:4.0.0-rc-2'
datagenImplementation 'org.apache.groovy:groovy:4.0.0-beta-2' datagenImplementation 'org.apache.groovy:groovy:4.0.0-rc-2'
gametestImplementation ("com.google.truth:truth:1.1.3") {
exclude module: "guava"
}
} }
def optionalDependency(String dep) { def optionalDependency(String dep) {

View file

@ -24,6 +24,7 @@
package techreborn.test package techreborn.test
import groovy.util.logging.Slf4j
import net.fabricmc.fabric.api.gametest.v1.FabricGameTest import net.fabricmc.fabric.api.gametest.v1.FabricGameTest
import net.minecraft.test.TestContext import net.minecraft.test.TestContext
@ -34,9 +35,25 @@ import java.lang.reflect.Method
* *
* All test methods should accept 1 argument of TRTestContext * All test methods should accept 1 argument of TRTestContext
*/ */
@Slf4j
abstract class TRGameTest implements FabricGameTest { abstract class TRGameTest implements FabricGameTest {
@Override @Override
void invokeTestMethod(TestContext context, Method method) { void invokeTestMethod(TestContext context, Method method) {
try {
method.invoke(this, new TRTestContext(context)) method.invoke(this, new TRTestContext(context))
} catch (TRGameTestException gameTestException) {
log.error("Test ${method.name} failed with message ${gameTestException.message}", gameTestException.cause)
log.error(gameTestException.cause.message)
throw gameTestException
} catch (Throwable throwable) {
log.error("Test ${method.name} failed", throwable)
throw throwable
}
}
static class TRGameTestException extends AssertionError {
TRGameTestException(String message, Throwable cause) {
super(message, cause)
}
} }
} }

View file

@ -0,0 +1,69 @@
/*
* This file is part of TechReborn, licensed under the MIT License (MIT).
*
* Copyright (c) 2020 TechReborn
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package techreborn.test.recipe
import com.google.common.truth.Truth
import net.minecraft.test.GameTest
import reborncore.common.crafting.RebornRecipe
import reborncore.common.crafting.RebornRecipeType
import reborncore.common.crafting.RecipeManager
import techreborn.test.TRGameTest
import techreborn.test.TRTestContext
/**
* A bit of a mess, but checks that we can ser/de all recipes to and from json.
*/
class RecipeSyncTests extends TRGameTest {
@GameTest(structureName = "fabric-gametest-api-v1:empty", tickLimit = 150)
def testRecipes(TRTestContext context) {
def recipeTypes = RecipeManager.getRecipeTypes("techreborn")
recipeTypes.each { type ->
def recipes = type.getRecipes(context.world)
recipes.each { recipe ->
try {
testRecipe(type, recipe)
} catch (Throwable t) {
throw new TRGameTestException(recipe.id.toString(), t)
}
}
}
context.complete()
}
def <R extends RebornRecipe> void testRecipe(RebornRecipeType<R> type, R recipe) {
def firstJson = type.toJson(recipe, true).deepCopy()
def newRecipe = type.read(recipe.id, firstJson)
def secondJson = type.toJson(newRecipe, true).deepCopy()
Truth.assertThat(firstJson.toString())
.isEqualTo(secondJson.toString())
// And check we can create a data json
def dataJson = type.toJson(newRecipe, false).deepCopy()
}
}

View file

@ -6,7 +6,8 @@
"environment": "*", "environment": "*",
"entrypoints": { "entrypoints": {
"fabric-gametest" : [ "fabric-gametest" : [
"techreborn.test.machine.GrinderTest" "techreborn.test.machine.GrinderTest",
"techreborn.test.recipe.RecipeSyncTests"
] ]
} }
} }

View file

@ -43,7 +43,7 @@ public class BlastFurnaceRecipeSerde extends RebornRecipeSerde<BlastFurnaceRecip
} }
@Override @Override
public void collectJsonData(BlastFurnaceRecipe recipe, JsonObject jsonObject) { public void collectJsonData(BlastFurnaceRecipe recipe, JsonObject jsonObject, boolean networkSync) {
jsonObject.addProperty("heat", recipe.getHeat()); jsonObject.addProperty("heat", recipe.getHeat());
} }
} }

View file

@ -44,7 +44,7 @@ public class FusionReactorRecipeSerde extends RebornRecipeSerde<FusionReactorRec
} }
@Override @Override
protected void collectJsonData(FusionReactorRecipe recipe, JsonObject jsonObject) { protected void collectJsonData(FusionReactorRecipe recipe, JsonObject jsonObject, boolean networkSync) {
jsonObject.addProperty("start-power", recipe.getStartEnergy()); jsonObject.addProperty("start-power", recipe.getStartEnergy());
jsonObject.addProperty("min-size", recipe.getMinSize()); jsonObject.addProperty("min-size", recipe.getMinSize());
} }

View file

@ -7,12 +7,14 @@ import net.minecraft.recipe.ShapedRecipe;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.JsonHelper; import net.minecraft.util.JsonHelper;
import reborncore.common.crafting.RebornRecipeType; import reborncore.common.crafting.RebornRecipeType;
import reborncore.common.crafting.ShapedRecipeHelper;
import reborncore.common.crafting.ingredient.RebornIngredient; import reborncore.common.crafting.ingredient.RebornIngredient;
import reborncore.common.crafting.serde.RebornRecipeSerde; import reborncore.common.crafting.serde.RebornRecipeSerde;
import techreborn.api.recipe.recipes.RollingMachineRecipe; import techreborn.api.recipe.recipes.RollingMachineRecipe;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Objects;
public class RollingMachineRecipeSerde extends RebornRecipeSerde<RollingMachineRecipe> { public class RollingMachineRecipeSerde extends RebornRecipeSerde<RollingMachineRecipe> {
@Override @Override
@ -23,8 +25,9 @@ public class RollingMachineRecipeSerde extends RebornRecipeSerde<RollingMachineR
} }
@Override @Override
public void collectJsonData(RollingMachineRecipe recipe, JsonObject jsonObject) { public void collectJsonData(RollingMachineRecipe recipe, JsonObject jsonObject, boolean networkSync) {
jsonObject.add("shaped", recipe.getShapedRecipeJson()); final JsonObject shapedRecipeJson = networkSync ? ShapedRecipeHelper.rewriteForNetworkSync(recipe.getShapedRecipeJson()) : recipe.getShapedRecipeJson();
jsonObject.add("shaped", Objects.requireNonNull(shapedRecipeJson));
} }
@Override @Override
@ -40,7 +43,7 @@ public class RollingMachineRecipeSerde extends RebornRecipeSerde<RollingMachineR
} }
@Override @Override
protected void writeIngredients(RollingMachineRecipe recipe, JsonObject jsonObject) { protected void writeIngredients(RollingMachineRecipe recipe, JsonObject jsonObject, boolean networkSync) {
} }
@Override @Override