mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-11-13 03:43:08 +00:00
Fix crash when joining a dedicated server
We can't use FriendlyByte.readCollection to read to a pre-allocated/array-backed NonNullList, as that doesn't implement List.add. Instead, we just need to do a normal loop. We add a couple of tests to round-trip our recipe specs. Unfortunately we can't test the recipes themselves as our own registries aren't set up, so this'll have to do for now.
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.recipe;
|
||||
|
||||
import dan200.computercraft.test.shared.MinecraftArbitraries;
|
||||
import it.unimi.dsi.fastutil.ints.IntIntImmutablePair;
|
||||
import net.jqwik.api.Arbitraries;
|
||||
import net.jqwik.api.Arbitrary;
|
||||
import net.jqwik.api.Combinators;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.world.item.crafting.CraftingBookCategory;
|
||||
import net.minecraft.world.item.crafting.Ingredient;
|
||||
|
||||
/**
|
||||
* {@link Arbitrary} implementations for recipes.
|
||||
*/
|
||||
public final class RecipeArbitraries {
|
||||
public static Arbitrary<RecipeProperties> recipeProperties() {
|
||||
return Combinators.combine(
|
||||
Arbitraries.strings().ofMinLength(1).withChars("abcdefghijklmnopqrstuvwxyz_"),
|
||||
Arbitraries.of(CraftingBookCategory.values())
|
||||
).as(RecipeProperties::new);
|
||||
}
|
||||
|
||||
public static Arbitrary<ShapelessRecipeSpec> shapelessRecipeSpec() {
|
||||
return Combinators.combine(
|
||||
recipeProperties(),
|
||||
MinecraftArbitraries.ingredient().array(Ingredient[].class).ofMinSize(1).map(x -> NonNullList.of(Ingredient.EMPTY, x)),
|
||||
MinecraftArbitraries.nonEmptyItemStack()
|
||||
).as(ShapelessRecipeSpec::new);
|
||||
}
|
||||
|
||||
public static Arbitrary<ShapedTemplate> shapedTemplate() {
|
||||
return Combinators.combine(Arbitraries.integers().between(1, 3), Arbitraries.integers().between(1, 3))
|
||||
.as(IntIntImmutablePair::new)
|
||||
.flatMap(x -> MinecraftArbitraries.ingredient().array(Ingredient[].class).ofSize(x.leftInt() * x.rightInt())
|
||||
.map(i -> new ShapedTemplate(x.leftInt(), x.rightInt(), NonNullList.of(Ingredient.EMPTY, i)))
|
||||
);
|
||||
}
|
||||
|
||||
public static Arbitrary<ShapedRecipeSpec> shapedRecipeSpec() {
|
||||
return Combinators.combine(
|
||||
recipeProperties(),
|
||||
shapedTemplate(),
|
||||
MinecraftArbitraries.nonEmptyItemStack()
|
||||
).as(ShapedRecipeSpec::new);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.recipe;
|
||||
|
||||
import dan200.computercraft.test.core.StructuralEquality;
|
||||
import dan200.computercraft.test.shared.MinecraftEqualities;
|
||||
|
||||
/**
|
||||
* {@link StructuralEquality} implementations for recipes.
|
||||
*/
|
||||
public final class RecipeEqualities {
|
||||
private RecipeEqualities() {
|
||||
}
|
||||
|
||||
public static final StructuralEquality<ShapelessRecipeSpec> shapelessRecipeSpec = StructuralEquality.all(
|
||||
StructuralEquality.at("properties", ShapelessRecipeSpec::properties),
|
||||
StructuralEquality.at("ingredients", ShapelessRecipeSpec::ingredients, MinecraftEqualities.ingredient.list()),
|
||||
StructuralEquality.at("result", ShapelessRecipeSpec::result, MinecraftEqualities.itemStack)
|
||||
);
|
||||
|
||||
public static final StructuralEquality<ShapedTemplate> shapedTemplate = StructuralEquality.all(
|
||||
StructuralEquality.at("width", ShapedTemplate::width),
|
||||
StructuralEquality.at("height", ShapedTemplate::height),
|
||||
StructuralEquality.at("ingredients", ShapedTemplate::ingredients, MinecraftEqualities.ingredient.list())
|
||||
);
|
||||
|
||||
public static final StructuralEquality<ShapedRecipeSpec> shapedRecipeSpec = StructuralEquality.all(
|
||||
StructuralEquality.at("properties", ShapedRecipeSpec::properties),
|
||||
StructuralEquality.at("ingredients", ShapedRecipeSpec::template, shapedTemplate),
|
||||
StructuralEquality.at("result", ShapedRecipeSpec::result, MinecraftEqualities.itemStack)
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.recipe;
|
||||
|
||||
import dan200.computercraft.test.shared.NetworkSupport;
|
||||
import dan200.computercraft.test.shared.WithMinecraft;
|
||||
import net.jqwik.api.Arbitrary;
|
||||
import net.jqwik.api.ForAll;
|
||||
import net.jqwik.api.Property;
|
||||
import net.jqwik.api.Provide;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
@WithMinecraft
|
||||
public class ShapedRecipeSpecTest {
|
||||
static {
|
||||
WithMinecraft.Setup.bootstrap(); // @Property doesn't run test lifecycle methods.
|
||||
}
|
||||
|
||||
@Property
|
||||
public void testRoundTrip(@ForAll("recipe") ShapedRecipeSpec spec) {
|
||||
var converted = NetworkSupport.roundTrip(spec, ShapedRecipeSpec::toNetwork, ShapedRecipeSpec::fromNetwork);
|
||||
assertThat("Recipes are equal", converted, RecipeEqualities.shapedRecipeSpec.asMatcher(ShapedRecipeSpec.class, spec));
|
||||
}
|
||||
|
||||
@Provide
|
||||
Arbitrary<ShapedRecipeSpec> recipe() {
|
||||
return RecipeArbitraries.shapedRecipeSpec();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.recipe;
|
||||
|
||||
import dan200.computercraft.test.shared.NetworkSupport;
|
||||
import dan200.computercraft.test.shared.WithMinecraft;
|
||||
import net.jqwik.api.Arbitrary;
|
||||
import net.jqwik.api.ForAll;
|
||||
import net.jqwik.api.Property;
|
||||
import net.jqwik.api.Provide;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
@WithMinecraft
|
||||
public class ShapelessRecipeSpecTest {
|
||||
static {
|
||||
WithMinecraft.Setup.bootstrap(); // @Property doesn't run test lifecycle methods.
|
||||
}
|
||||
|
||||
@Property
|
||||
public void testRoundTrip(@ForAll("recipe") ShapelessRecipeSpec spec) {
|
||||
var converted = NetworkSupport.roundTrip(spec, ShapelessRecipeSpec::toNetwork, ShapelessRecipeSpec::fromNetwork);
|
||||
assertThat("Recipes are equal", converted, RecipeEqualities.shapelessRecipeSpec.asMatcher(ShapelessRecipeSpec.class, spec));
|
||||
}
|
||||
|
||||
@Provide
|
||||
Arbitrary<ShapelessRecipeSpec> recipe() {
|
||||
return RecipeArbitraries.shapelessRecipeSpec();
|
||||
}
|
||||
}
|
||||
@@ -8,16 +8,13 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleToolDurability;
|
||||
import dan200.computercraft.test.core.StructuralEquality;
|
||||
import dan200.computercraft.test.shared.MinecraftArbitraries;
|
||||
import dan200.computercraft.test.shared.MinecraftEqualities;
|
||||
import dan200.computercraft.test.shared.NetworkSupport;
|
||||
import dan200.computercraft.test.shared.WithMinecraft;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.jqwik.api.*;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.hamcrest.Description;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@WithMinecraft
|
||||
class TurtleToolSerialiserTest {
|
||||
@@ -32,11 +29,9 @@ class TurtleToolSerialiserTest {
|
||||
*/
|
||||
@Property
|
||||
public void testRoundTrip(@ForAll("tool") TurtleTool tool) {
|
||||
var buffer = new FriendlyByteBuf(Unpooled.directBuffer());
|
||||
TurtleToolSerialiser.INSTANCE.toNetwork(buffer, tool);
|
||||
|
||||
var converted = TurtleToolSerialiser.INSTANCE.fromNetwork(tool.getUpgradeID(), buffer);
|
||||
assertEquals(buffer.readableBytes(), 0, "Whole packet was read");
|
||||
var converted = NetworkSupport.roundTripSerialiser(
|
||||
tool.getUpgradeID(), tool, TurtleToolSerialiser.INSTANCE::toNetwork, TurtleToolSerialiser.INSTANCE::fromNetwork
|
||||
);
|
||||
|
||||
if (!equality.equals(tool, converted)) {
|
||||
System.out.println("Break");
|
||||
@@ -58,22 +53,10 @@ class TurtleToolSerialiserTest {
|
||||
).as(TurtleTool::new);
|
||||
}
|
||||
|
||||
private static final StructuralEquality<ItemStack> stackEquality = new StructuralEquality<>() {
|
||||
@Override
|
||||
public boolean equals(ItemStack left, ItemStack right) {
|
||||
return ItemStack.isSameItemSameTags(left, right) && left.getCount() == right.getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describe(Description description, ItemStack object) {
|
||||
description.appendValue(object).appendValue(object.getTag());
|
||||
}
|
||||
};
|
||||
|
||||
private static final StructuralEquality<TurtleTool> equality = StructuralEquality.all(
|
||||
StructuralEquality.at("id", ITurtleUpgrade::getUpgradeID),
|
||||
StructuralEquality.at("craftingItem", ITurtleUpgrade::getCraftingItem, stackEquality),
|
||||
StructuralEquality.at("tool", x -> x.item, stackEquality),
|
||||
StructuralEquality.at("craftingItem", ITurtleUpgrade::getCraftingItem, MinecraftEqualities.itemStack),
|
||||
StructuralEquality.at("tool", x -> x.item, MinecraftEqualities.itemStack),
|
||||
StructuralEquality.at("damageMulitiplier", x -> x.damageMulitiplier),
|
||||
StructuralEquality.at("allowEnchantments", x -> x.allowEnchantments),
|
||||
StructuralEquality.at("consumeDurability", x -> x.consumeDurability),
|
||||
|
||||
Reference in New Issue
Block a user