9
0
mirror of https://github.com/Xiao-MoMi/craft-engine.git synced 2025-12-19 15:09:15 +00:00

Merge pull request #49 from Xiao-MoMi/dev

0.0.35
This commit is contained in:
XiaoMoMi
2025-03-20 04:29:08 +08:00
committed by GitHub
66 changed files with 1127 additions and 115 deletions

View File

@@ -135,7 +135,7 @@ repositories {
```
```kotlin
dependencies {
compileOnly("net.momirealms:craft-engine-core:0.0.29")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.29")
compileOnly("net.momirealms:craft-engine-core:0.0.34")
compileOnly("net.momirealms:craft-engine-bukkit:0.0.34")
}
```

View File

@@ -1,5 +1,5 @@
plugins {
id("com.gradleup.shadow") version "9.0.0-beta6"
id("com.gradleup.shadow") version "9.0.0-beta11"
id("net.minecrell.plugin-yml.bukkit") version "0.6.0"
}

View File

@@ -26,8 +26,9 @@ resource-pack:
obfuscation:
enable: false
seed: 0 # 0 = random seed
fake-directory: true
escape-unicode: true
fake-directory: false
escape-unicode: false
break-json: false
resource-location:
enable: true
random-namespace:
@@ -183,12 +184,14 @@ gui:
title: "<white><shift:-11><image:internal:cooking_recipe><shift:-136><image:internal:smelting>"
smoking:
title: "<white><shift:-11><image:internal:cooking_recipe><shift:-136><image:internal:smoking>"
campfire:
campfire-cooking:
title: "<white><shift:-11><image:internal:cooking_recipe><shift:-136><image:internal:campfire>"
crafting:
title: "<white><shift:-11><image:internal:crafting_recipe>"
stonecutting:
title: "<white><shift:-11><image:internal:stonecutting_recipe>"
smithing-transform:
title: "<white><shift:-11><image:internal:smithing_transform_recipe>"
performance:
# Maximum chain update depth when fixing client visuals

View File

@@ -329,4 +329,70 @@ recipes#11:
B: "minecraft:stick"
result:
id: default:topaz_pickaxe
count: 1
default:topaz_helmet:
type: shaped
pattern:
- "AAA"
- "A A"
ingredients:
A: "default:topaz"
result:
id: default:topaz_helmet
count: 1
default:topaz_chestplate:
type: shaped
pattern:
- "A A"
- "AAA"
- "AAA"
ingredients:
A: "default:topaz"
result:
id: default:topaz_chestplate
count: 1
default:topaz_leggings:
type: shaped
pattern:
- "AAA"
- "A A"
- "A A"
ingredients:
A: "default:topaz"
result:
id: default:topaz_leggings
count: 1
default:topaz_boots:
type: shaped
pattern:
- "A A"
- "A A"
ingredients:
A: "default:topaz"
result:
id: default:topaz_boots
count: 1
default:topaz_bow:
type: smithing_transform
base: minecraft:bow
addition: default:topaz
template-type: default:topaz
result:
id: default:topaz_bow
count: 1
default:topaz_crossbow:
type: smithing_transform
base: minecraft:crossbow
addition: default:topaz
template-type: default:topaz
result:
id: default:topaz_crossbow
count: 1
default:topaz_rod:
type: smithing_transform
base: minecraft:fishing_rod
addition: default:topaz
template-type: default:topaz
result:
id: default:topaz_rod
count: 1

View File

@@ -314,12 +314,12 @@ recipes:
default:palm_planks:
template: default:recipe/planks
arguments:
tree_type: palm
wood_type: palm
default:palm_wood:
template: default:recipe/log_2_wood
arguments:
tree_type: palm
wood_type: palm
default:stripped_palm_wood:
template: default:recipe/log_2_wood
arguments:
tree_type: stripped_palm
wood_type: stripped_palm

View File

@@ -33,9 +33,31 @@ templates#models#block:
textures:
"end": "{end_texture}"
"side": "{side_texture}"
# template: default:model/cube
# arguments:
# model: model_path
# particle_texture: particle_texture_path
# down_texture: down_texture_path
# up_texture: up_texture_path
# north_texture: north_texture_path
# east_texture: east_texture_path
# south_texture: south_texture_path
# west_texture: west_texture_path
default:model/cube:
path: "{model}"
generation:
parent: "minecraft:block/cube_column"
textures:
"particle": "minecraft:block/custom/block_particle"
"down": "minecraft:block/custom/block_down"
"up": "minecraft:block/custom/block_up"
"north": "minecraft:block/custom/block_north"
"east": "minecraft:block/custom/block_east"
"south": "minecraft:block/custom/block_south"
"west": "minecraft:block/custom/block_west"
# 2D items
templates#models#generated:
templates#models#2d:
# template: default:model/generated
# arguments:
# model: model_path
@@ -92,6 +114,22 @@ templates#models#generated:
textures:
"layer0": "{path}"
# shield
templates#models#shield:
# template: default:model/shield_3d
# arguments:
# model: shield_model_path
# block_model: shield_block_model_path
default:model/shield_3d:
type: "minecraft:condition"
property: "minecraft:using_item"
on-false:
type: minecraft:model
path: "{model}"
on-true:
type: minecraft:model
path: "{block_model}"
# fishing rods
templates#models#fishing_rod:
# template: default:model/fishing_rod_3d
@@ -258,14 +296,14 @@ templates#models#crossbow:
- when: arrow
model:
type: minecraft:model
path: "{arrow_path}"
path: "{arrow_model}"
- when: rocket
model:
type: minecraft:model
path: "{firework_model}"
fallback:
type: minecraft:model
path: "{path}"
path: "{model}"
on-true:
type: "minecraft:range_dispatch"
property: "minecraft:crossbow/pull"
@@ -305,7 +343,7 @@ templates#models#crossbow:
- when: arrow
model:
type: minecraft:model
path: "{arrow_path}"
path: "{arrow_model}"
generation:
parent: "minecraft:item/crossbow_arrow"
textures:
@@ -320,7 +358,7 @@ templates#models#crossbow:
"layer0": "{firework_texture}"
fallback:
type: minecraft:model
path: "{path}"
path: "{model}"
generation:
parent: "minecraft:item/crossbow"
textures:
@@ -339,7 +377,7 @@ templates#models#crossbow:
threshold: 0.58
- model:
type: minecraft:model
path: "{pulling_2_path}"
path: "{pulling_2_model}"
generation:
parent: "minecraft:item/crossbow_pulling_2"
textures:
@@ -862,9 +900,9 @@ templates#recipes:
category: building
group: planks
ingredients:
A: "#default:{tree_type}_logs"
A: "#default:{wood_type}_logs"
result:
id: "default:{tree_type}_planks"
id: "default:{wood_type}_planks"
count: 4
default:recipe/log_2_wood:
type: shaped
@@ -874,9 +912,9 @@ templates#recipes:
- "AA"
- "AA"
ingredients:
A: "default:{tree_type}_log"
A: "default:{wood_type}_log"
result:
id: "default:{tree_type}_wood"
id: "default:{wood_type}_wood"
count: 3
default:recipe/smelting_ore:
type: smelting

View File

@@ -1,7 +1,8 @@
{
"texture_size": [64, 64],
"textures": {
"0": "item/custom/bench"
"0": "item/custom/bench",
"particle": "item/custom/bench"
},
"elements": [
{

View File

@@ -1,7 +1,8 @@
{
"texture_size": [32, 32],
"textures": {
"0": "item/custom/table_lamp"
"0": "item/custom/table_lamp",
"particle": "item/custom/table_lamp"
},
"elements": [
{

View File

@@ -1,7 +1,8 @@
{
"texture_size": [32, 32],
"textures": {
"0": "item/custom/wooden_chair"
"0": "item/custom/wooden_chair",
"particle": "item/custom/wooden_chair"
},
"elements": [
{

View File

@@ -53,12 +53,18 @@ images:
font: minecraft:internal
file: minecraft:font/gui/custom/stonecutting_recipe.png
char: '\ub008'
internal:smithing_transform_recipe:
height: 142
ascent: 20
font: minecraft:internal
file: minecraft:font/gui/custom/smithing_transform_recipe.png
char: '\ub009'
internal:no_recipe:
height: 140
ascent: 18
font: minecraft:internal
file: minecraft:font/gui/custom/no_recipe.png
char: '\ub009'
char: '\ub00a'
templates:
internal:icon/2d:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,5 +1,5 @@
plugins {
id("com.gradleup.shadow") version "9.0.0-beta6"
id("com.gradleup.shadow") version "9.0.0-beta11"
id("maven-publish")
}

View File

@@ -1,5 +1,5 @@
plugins {
id("io.github.goooler.shadow") version "8.1.8"
id("com.gradleup.shadow") version "9.0.0-beta11"
}
repositories {

View File

@@ -343,9 +343,7 @@ public class BukkitFurnitureManager implements FurnitureManager {
return;
}
Vector3f seatPos = MiscUtils.getVector3f(vector3f);
if (!furniture.releaseSeat(seatPos)) {
plugin.logger().warn("Failed to release seat " + seatPos + " for player " + player.getName());
}
furniture.releaseSeat(seatPos);
}
protected boolean isSeatCarrierType(Entity entity) {

View File

@@ -4,6 +4,8 @@ import com.saicone.rtag.RtagItem;
import net.momirealms.craftengine.core.item.ItemWrapper;
import org.bukkit.inventory.ItemStack;
import java.util.Map;
@SuppressWarnings("UnstableApiUsage")
public class RTagItemWrapper implements ItemWrapper<ItemStack> {
private final RtagItem rtagItem;
@@ -110,4 +112,9 @@ public class RTagItemWrapper implements ItemWrapper<ItemStack> {
public ItemWrapper<ItemStack> copyWithCount(int count) {
return new RTagItemWrapper(new RtagItem(this.rtagItem.loadCopy()), count);
}
@Override
public Map<String, Object> getData() {
return this.rtagItem.get();
}
}

View File

@@ -88,7 +88,7 @@ public class AxeItemBehavior extends ItemBehavior {
if (!InteractUtils.isInteractable(BlockStateUtils.getBlockOwnerIdFromState(state.vanillaBlockState().handle()),
bukkitPlayer, BlockStateUtils.fromBlockData(state.vanillaBlockState().handle()),
context.getHitResult(), item
)) {
) || player.isSecondaryUseActive()) {
player.swingHand(context.getHand());
}
// shrink item amount

View File

@@ -1,7 +1,11 @@
package net.momirealms.craftengine.bukkit.item.factory;
import com.saicone.rtag.RtagItem;
import com.saicone.rtag.data.ComponentType;
import com.saicone.rtag.item.ItemObject;
import net.momirealms.craftengine.bukkit.item.RTagItemWrapper;
import net.momirealms.craftengine.bukkit.util.EnchantmentUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.item.ComponentKeys;
import net.momirealms.craftengine.core.item.Enchantment;
import net.momirealms.craftengine.core.item.ItemWrapper;
@@ -287,4 +291,18 @@ public class ComponentItemFactory extends BukkitItemFactory {
if (!item.hasComponent(ComponentKeys.REPAIR_COST)) return Optional.empty();
return Optional.ofNullable((Integer) ComponentType.encodeJava(ComponentKeys.REPAIR_COST, item.getComponent(ComponentKeys.REPAIR_COST)).orElse(null));
}
@Override
protected ItemWrapper<ItemStack> merge(ItemWrapper<ItemStack> item1, ItemWrapper<ItemStack> item2) {
Object itemStack1 = item1.getLiteralObject();
Object itemStack2 = item2.getLiteralObject();
try {
Object itemStack3 = Reflections.method$ItemStack$transmuteCopy.invoke(itemStack1, Reflections.method$ItemStack$getItem.invoke(itemStack2), 1);
Reflections.method$ItemStack$applyComponents.invoke(itemStack3, Reflections.method$ItemStack$getComponentsPatch.invoke(itemStack2));
return new RTagItemWrapper(new RtagItem(ItemObject.asCraftMirror(itemStack3)), item2.count());
} catch (Exception e) {
this.plugin.logger().warn("Failed to merge item", e);
}
return null;
}
}

View File

@@ -1,8 +1,11 @@
package net.momirealms.craftengine.bukkit.item.factory;
import com.saicone.rtag.RtagItem;
import com.saicone.rtag.item.ItemObject;
import com.saicone.rtag.tag.TagBase;
import com.saicone.rtag.tag.TagCompound;
import com.saicone.rtag.tag.TagList;
import net.momirealms.craftengine.bukkit.item.RTagItemWrapper;
import net.momirealms.craftengine.core.item.Enchantment;
import net.momirealms.craftengine.core.item.ItemWrapper;
import net.momirealms.craftengine.core.plugin.CraftEngine;
@@ -123,18 +126,11 @@ public class UniversalItemFactory extends BukkitItemFactory {
@Override
protected Optional<Integer> maxDamage(ItemWrapper<ItemStack> item) {
// if (!item.hasTag("CustomFishing", "max_dur")) return Optional.empty();
// return Optional.of(item.get("CustomFishing", "max_dur"));
return Optional.of((int) item.getItem().getType().getMaxDurability());
}
@Override
protected void maxDamage(ItemWrapper<ItemStack> item, Integer damage) {
// if (damage == null) {
// item.remove("CustomFishing", "max_dur");
// } else {
// item.set(damage, "CustomFishing", "max_dur");
// }
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@@ -217,6 +213,7 @@ public class UniversalItemFactory extends BukkitItemFactory {
@Override
protected void maxStackSize(ItemWrapper<ItemStack> item, Integer maxStackSize) {
throw new UnsupportedOperationException("This feature is only available on 1.20.5+");
}
@Override
@@ -229,4 +226,13 @@ public class UniversalItemFactory extends BukkitItemFactory {
if (!item.hasTag("RepairCost")) return Optional.empty();
return Optional.of(item.get("RepairCost"));
}
@Override
protected ItemWrapper<ItemStack> merge(ItemWrapper<ItemStack> item1, ItemWrapper<ItemStack> item2) {
Object itemStack = ItemObject.copy(item2.getLiteralObject());
ItemObject.setCustomDataTag(itemStack, TagCompound.clone(ItemObject.getCustomDataTag(item1.getLiteralObject())));
// one more step than vanilla
TagCompound.merge(ItemObject.getCustomDataTag(itemStack), ItemObject.getCustomDataTag(item2.getLiteralObject()), true, true);
return new RTagItemWrapper(new RtagItem(ItemObject.asCraftMirror(itemStack)), item2.count());
}
}

View File

@@ -54,6 +54,22 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
private static BukkitRecipeManager instance;
static {
BUKKIT_RECIPE_FACTORIES.put(RecipeTypes.SMITHING_TRANSFORM, (key, recipe) -> {
CustomSmithingTransformRecipe<ItemStack> ceRecipe = (CustomSmithingTransformRecipe<ItemStack>) recipe;
ceRecipe.addition();
// bukkit api doesn't allow empty material choices, that's why we do this
try {
Object smithingRecipe = createMinecraftSmithingTransformRecipe(ceRecipe);
if (VersionHelper.isVersionNewerThan1_21_2()) {
smithingRecipe = Reflections.constructor$RecipeHolder.newInstance(Reflections.method$CraftRecipe$toMinecraft.invoke(null, key), smithingRecipe);
} else if (VersionHelper.isVersionNewerThan1_20_2()) {
smithingRecipe = Reflections.constructor$RecipeHolder.newInstance(Reflections.method$ResourceLocation$fromNamespaceAndPath.invoke(null, key.namespace(), key.value()), smithingRecipe);
}
Reflections.method$RecipeManager$addRecipe.invoke(minecraftRecipeManager(), smithingRecipe);
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to convert transform recipe", e);
}
});
BUKKIT_RECIPE_FACTORIES.put(RecipeTypes.SHAPED, (key, recipe) -> {
CustomShapedRecipe<ItemStack> ceRecipe = (CustomShapedRecipe<ItemStack>) recipe;
ShapedRecipe shapedRecipe = new ShapedRecipe(key, ceRecipe.result(ItemBuildContext.EMPTY));
@@ -65,7 +81,7 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
}
shapedRecipe.shape(ceRecipe.pattern().pattern());
for (Map.Entry<Character, Ingredient<ItemStack>> entry : ceRecipe.pattern().ingredients().entrySet()) {
shapedRecipe.setIngredient(entry.getKey(), new RecipeChoice.MaterialChoice(ingredientToBukkitMaterials(entry.getValue())));
shapedRecipe.setIngredient(entry.getKey(), ingredientToBukkitRecipeChoice(entry.getValue()));
}
try {
Object craftRecipe = Reflections.method$CraftShapedRecipe$fromBukkitRecipe.invoke(null, shapedRecipe);
@@ -85,7 +101,7 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
shapelessRecipe.setCategory(CraftingBookCategory.valueOf(Objects.requireNonNull(ceRecipe.category()).name()));
}
for (Ingredient<ItemStack> ingredient : ceRecipe.ingredientsInUse()) {
shapelessRecipe.addIngredient(new RecipeChoice.MaterialChoice(ingredientToBukkitMaterials(ingredient)));
shapelessRecipe.addIngredient(ingredientToBukkitRecipeChoice(ingredient));
}
try {
Object craftRecipe = Reflections.method$CraftShapelessRecipe$fromBukkitRecipe.invoke(null, shapelessRecipe);
@@ -99,7 +115,7 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
CustomSmeltingRecipe<ItemStack> ceRecipe = (CustomSmeltingRecipe<ItemStack>) recipe;
FurnaceRecipe furnaceRecipe = new FurnaceRecipe(
key, ceRecipe.result(ItemBuildContext.EMPTY),
new RecipeChoice.MaterialChoice(ingredientToBukkitMaterials(ceRecipe.ingredient())),
ingredientToBukkitRecipeChoice(ceRecipe.ingredient()),
ceRecipe.experience(), ceRecipe.cookingTime()
);
if (ceRecipe.group() != null) {
@@ -120,7 +136,7 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
CustomSmokingRecipe<ItemStack> ceRecipe = (CustomSmokingRecipe<ItemStack>) recipe;
SmokingRecipe smokingRecipe = new SmokingRecipe(
key, ceRecipe.result(ItemBuildContext.EMPTY),
new RecipeChoice.MaterialChoice(ingredientToBukkitMaterials(ceRecipe.ingredient())),
ingredientToBukkitRecipeChoice(ceRecipe.ingredient()),
ceRecipe.experience(), ceRecipe.cookingTime()
);
if (ceRecipe.group() != null) {
@@ -141,7 +157,7 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
CustomBlastingRecipe<ItemStack> ceRecipe = (CustomBlastingRecipe<ItemStack>) recipe;
BlastingRecipe blastingRecipe = new BlastingRecipe(
key, ceRecipe.result(ItemBuildContext.EMPTY),
new RecipeChoice.MaterialChoice(ingredientToBukkitMaterials(ceRecipe.ingredient())),
ingredientToBukkitRecipeChoice(ceRecipe.ingredient()),
ceRecipe.experience(), ceRecipe.cookingTime()
);
if (ceRecipe.group() != null) {
@@ -162,7 +178,7 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
CustomCampfireRecipe<ItemStack> ceRecipe = (CustomCampfireRecipe<ItemStack>) recipe;
CampfireRecipe campfireRecipe = new CampfireRecipe(
key, ceRecipe.result(ItemBuildContext.EMPTY),
new RecipeChoice.MaterialChoice(ingredientToBukkitMaterials(ceRecipe.ingredient())),
ingredientToBukkitRecipeChoice(ceRecipe.ingredient()),
ceRecipe.experience(), ceRecipe.cookingTime()
);
if (ceRecipe.group() != null) {
@@ -179,7 +195,7 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
CraftEngine.instance().logger().warn("Failed to convert campfire recipe", e);
}
});
BUKKIT_RECIPE_FACTORIES.put(RecipeTypes.STONE_CUTTING, (key, recipe) -> {
BUKKIT_RECIPE_FACTORIES.put(RecipeTypes.STONECUTTING, (key, recipe) -> {
CustomStoneCuttingRecipe<ItemStack> ceRecipe = (CustomStoneCuttingRecipe<ItemStack>) recipe;
List<ItemStack> itemStacks = new ArrayList<>();
for (Holder<Key> item : ceRecipe.ingredient().items()) {
@@ -366,9 +382,13 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
this.byType.computeIfAbsent(recipe.type(), k -> new ArrayList<>()).add(recipe);
this.byId.put(id, recipe);
this.byResult.computeIfAbsent(recipe.result().item().id(), k -> new ArrayList<>()).add(recipe);
HashSet<Key> usedKeys = new HashSet<>();
for (Ingredient<ItemStack> ingredient : recipe.ingredientsInUse()) {
for (Holder<Key> holder : ingredient.items()) {
this.byIngredient.computeIfAbsent(holder.value(), k -> new ArrayList<>()).add(recipe);
Key key = holder.value();
if (usedKeys.add(key)) {
this.byIngredient.computeIfAbsent(key, k -> new ArrayList<>()).add(recipe);
}
}
}
}
@@ -492,6 +512,10 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
VanillaStoneCuttingRecipe recipe = this.recipeReader.readStoneCutting(jsonObject);
handleDataPackStoneCuttingRecipe(id, recipe);
}
case "minecraft:smithing_transform" -> {
VanillaSmithingTransformRecipe recipe = this.recipeReader.readSmithingTransform(jsonObject);
handleDataPackSmithingTransform(id, recipe, (injectLogics::add));
}
}
}
}
@@ -517,6 +541,29 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
return future;
}
private boolean readVanillaIngredients(boolean hasCustomItemInTag, List<String> ingredients, Consumer<Material> materialConsumer, Consumer<Holder<Key>> holderConsumer) {
for (String item : ingredients) {
if (item.charAt(0) == '#') {
Key tag = Key.from(item.substring(1));
for (Material material : tagToMaterials(tag)) {
materialConsumer.accept(material);
}
if (!hasCustomItemInTag) {
if (!plugin.itemManager().tagToCustomItems(tag).isEmpty()) {
hasCustomItemInTag = true;
}
}
for (Holder<Key> holder : plugin.itemManager().tagToItems(tag)) {
holderConsumer.accept(holder);
}
} else {
materialConsumer.accept(MaterialUtils.getMaterial(item));
holderConsumer.accept(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.from(item)).orElseThrow());
}
}
return hasCustomItemInTag;
}
private void handleDataPackShapelessRecipe(Key id, VanillaShapelessRecipe recipe, Consumer<Runnable> callback) {
NamespacedKey key = new NamespacedKey(id.namespace(), id.value());
ItemStack result = createResultStack(recipe.result());
@@ -666,22 +713,7 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
Set<Material> materials = new HashSet<>();
Set<Holder<Key>> holders = new HashSet<>();
boolean hasCustomItemInTag = false;
for (String item : recipe.ingredient()) {
if (item.charAt(0) == '#') {
Key tag = Key.from(item.substring(1));
materials.addAll(tagToMaterials(tag));
if (!hasCustomItemInTag) {
if (!plugin.itemManager().tagToCustomItems(tag).isEmpty()) {
hasCustomItemInTag = true;
}
}
holders.addAll(plugin.itemManager().tagToItems(tag));
} else {
materials.add(MaterialUtils.getMaterial(item));
holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.from(item)).orElseThrow());
}
}
boolean hasCustomItemInTag = readVanillaIngredients(false, recipe.ingredient(), materials::add, holders::add);
org.bukkit.inventory.CookingRecipe<?> cookingRecipe = constructor1.apply(key, result, new RecipeChoice.MaterialChoice(new ArrayList<>(materials)), recipe.experience(), recipe.cookingTime());
if (recipe.group() != null) {
cookingRecipe.setGroup(recipe.group());
@@ -714,6 +746,53 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
this.addInternalRecipe(id, ceRecipe);
}
private void handleDataPackSmithingTransform(Key id, VanillaSmithingTransformRecipe recipe, Consumer<Runnable> callback) {
NamespacedKey key = new NamespacedKey(id.namespace(), id.value());
ItemStack result = createResultStack(recipe.result());
boolean hasCustomItemInTag;
Set<Material> additionMaterials = new HashSet<>();
Set<Holder<Key>> additionHolders = new HashSet<>();
hasCustomItemInTag = readVanillaIngredients(false, recipe.addition(), additionMaterials::add, additionHolders::add);
Set<Material> templateMaterials = new HashSet<>();
Set<Holder<Key>> templateHolders = new HashSet<>();
hasCustomItemInTag = readVanillaIngredients(hasCustomItemInTag, recipe.template(), templateMaterials::add, templateHolders::add);
Set<Material> baseMaterials = new HashSet<>();
Set<Holder<Key>> baseHolders = new HashSet<>();
hasCustomItemInTag = readVanillaIngredients(hasCustomItemInTag, recipe.base(), baseMaterials::add, baseHolders::add);
CustomSmithingTransformRecipe<ItemStack> ceRecipe = new CustomSmithingTransformRecipe<>(
id,
baseHolders.isEmpty() ? null : Ingredient.of(baseHolders),
templateHolders.isEmpty() ? null : Ingredient.of(templateHolders),
additionHolders.isEmpty() ? null : Ingredient.of(additionHolders),
new CustomRecipeResult<>(new CloneableConstantItem(recipe.result().isCustom() ? Key.of("!internal:custom") : Key.of(recipe.result().id()), result), recipe.result().count()),
true,
List.of()
);
SmithingTransformRecipe transformRecipe = new SmithingTransformRecipe(key, result,
new RecipeChoice.MaterialChoice(new ArrayList<>(templateMaterials)),
new RecipeChoice.MaterialChoice(new ArrayList<>(baseMaterials)),
new RecipeChoice.MaterialChoice(new ArrayList<>(additionMaterials))
);
if (hasCustomItemInTag) {
callback.accept(() -> {
try {
unregisterRecipe(key);
Reflections.method$CraftRecipe$addToCraftingManager.invoke(Reflections.method$CraftSmithingTransformRecipe$fromBukkitRecipe.invoke(null, transformRecipe));
} catch (Exception e) {
CraftEngine.instance().logger().warn("Failed to convert transform recipe", e);
}
});
this.injectedDataPackRecipes.add(key);
}
this.addInternalRecipe(id, ceRecipe);
}
private List<Material> tagToMaterials(Key tag) {
Set<Material> materials = new HashSet<>();
List<Holder<Key>> holders = this.plugin.itemManager().tagToVanillaItems(tag);
@@ -763,12 +842,12 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
return Key.of(prefix, fileName);
}
private static List<Material> ingredientToBukkitMaterials(Ingredient<ItemStack> ingredient) {
private static RecipeChoice ingredientToBukkitRecipeChoice(Ingredient<ItemStack> ingredient) {
Set<Material> materials = new HashSet<>();
for (Holder<Key> holder : ingredient.items()) {
materials.add(getMaterialById(holder.value()));
}
return new ArrayList<>(materials);
return new RecipeChoice.MaterialChoice(new ArrayList<>(materials));
}
private static Material getMaterialById(Key key) {
@@ -888,6 +967,53 @@ public class BukkitRecipeManager implements RecipeManager<ItemStack> {
}
}
// 1.20-1.21.2
private static Object toMinecraftIngredient(Ingredient<ItemStack> ingredient) throws ReflectiveOperationException {
if (ingredient == null) {
return Reflections.method$CraftRecipe$toIngredient.invoke(null, null, true);
} else {
RecipeChoice choice = ingredientToBukkitRecipeChoice(ingredient);
return Reflections.method$CraftRecipe$toIngredient.invoke(null, choice, true);
}
}
// 1.21.2+
private static Optional<Object> toOptionalMinecraftIngredient(Ingredient<ItemStack> ingredient) throws ReflectiveOperationException {
if (ingredient == null) {
return Optional.empty();
} else {
RecipeChoice choice = ingredientToBukkitRecipeChoice(ingredient);
Object mcIngredient = Reflections.method$CraftRecipe$toIngredient.invoke(null, choice, true);
return Optional.of(mcIngredient);
}
}
private static Object createMinecraftSmithingTransformRecipe(CustomSmithingTransformRecipe<ItemStack> ceRecipe) throws ReflectiveOperationException {
if (VersionHelper.isVersionNewerThan1_21_2()) {
return Reflections.constructor$SmithingTransformRecipe.newInstance(
toOptionalMinecraftIngredient(ceRecipe.template()),
toOptionalMinecraftIngredient(ceRecipe.base()),
toOptionalMinecraftIngredient(ceRecipe.addition()),
Reflections.method$CraftItemStack$asNMSMirror.invoke(null, ceRecipe.result(ItemBuildContext.EMPTY))
);
} else if (VersionHelper.isVersionNewerThan1_20_2()) {
return Reflections.constructor$SmithingTransformRecipe.newInstance(
toMinecraftIngredient(ceRecipe.template()),
toMinecraftIngredient(ceRecipe.base()),
toMinecraftIngredient(ceRecipe.addition()),
Reflections.method$CraftItemStack$asNMSMirror.invoke(null, ceRecipe.result(ItemBuildContext.EMPTY))
);
} else {
return Reflections.constructor$SmithingTransformRecipe.newInstance(
Reflections.method$ResourceLocation$fromNamespaceAndPath.invoke(null, ceRecipe.id().namespace(), ceRecipe.id().value()),
toMinecraftIngredient(ceRecipe.template()),
toMinecraftIngredient(ceRecipe.base()),
toMinecraftIngredient(ceRecipe.addition()),
Reflections.method$CraftItemStack$asNMSMirror.invoke(null, ceRecipe.result(ItemBuildContext.EMPTY))
);
}
}
public Object getRecipeHolderByRecipe(Recipe<ItemStack> recipe) {
return recipeToMcRecipeHolder.get(recipe);
}

View File

@@ -11,12 +11,11 @@ import net.momirealms.craftengine.bukkit.util.ItemUtils;
import net.momirealms.craftengine.bukkit.util.LegacyInventoryUtils;
import net.momirealms.craftengine.bukkit.util.Reflections;
import net.momirealms.craftengine.core.item.*;
import net.momirealms.craftengine.core.item.recipe.CustomCampfireRecipe;
import net.momirealms.craftengine.core.item.recipe.OptimizedIDItem;
import net.momirealms.craftengine.core.item.recipe.Recipe;
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
import net.momirealms.craftengine.core.item.recipe.*;
import net.momirealms.craftengine.core.item.recipe.input.CraftingInput;
import net.momirealms.craftengine.core.item.recipe.input.SingleItemInput;
import net.momirealms.craftengine.core.item.recipe.input.SmithingInput;
import net.momirealms.craftengine.core.plugin.config.ConfigManager;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
@@ -38,6 +37,7 @@ import org.bukkit.event.inventory.*;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.*;
import org.bukkit.inventory.view.AnvilView;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@@ -832,4 +832,71 @@ public class RecipeEventListener implements Listener {
plugin.logger().warn("Failed to correct used recipe", e);
}
}
@EventHandler(ignoreCancelled = true)
public void onSmithingTransform(PrepareSmithingEvent event) {
if (!ConfigManager.enableRecipeSystem()) return;
SmithingInventory inventory = event.getInventory();
if (!(inventory.getRecipe() instanceof SmithingTransformRecipe recipe)) return;
Key recipeId = Key.of(recipe.getKey().namespace(), recipe.getKey().value());
boolean isCustom = this.recipeManager.isCustomRecipe(recipeId);
// Maybe it's recipe from other plugins, then we ignore it
if (!isCustom) {
return;
}
ItemStack base = inventory.getInputEquipment();
ItemStack template = inventory.getInputTemplate();
ItemStack addition = inventory.getInputMineral();
SmithingInput<ItemStack> input = new SmithingInput<>(
getOptimizedIDItem(base),
getOptimizedIDItem(template),
getOptimizedIDItem(addition)
);
Recipe<ItemStack> ceRecipe = this.recipeManager.getRecipe(RecipeTypes.SMITHING_TRANSFORM, input);
if (ceRecipe == null) {
event.setResult(null);
return;
}
Player player;
try {
player = (Player) Reflections.method$InventoryView$getPlayer.invoke(event.getView());
} catch (ReflectiveOperationException e) {
this.plugin.logger().warn("Failed to get inventory viewer", e);
return;
}
CustomSmithingTransformRecipe<ItemStack> transformRecipe = (CustomSmithingTransformRecipe<ItemStack>) ceRecipe;
ItemStack processed = transformRecipe.assemble(new ItemBuildContext(this.plugin.adapt(player), ContextHolder.EMPTY), this.itemManager.wrap(base));
event.setResult(processed);
correctSmithingRecipeUsed(inventory, ceRecipe);
}
private void correctSmithingRecipeUsed(SmithingInventory inventory, Recipe<ItemStack> recipe) {
Object holderOrRecipe = recipeManager.getRecipeHolderByRecipe(recipe);
if (holderOrRecipe == null) {
// it's a vanilla recipe but not injected
return;
}
try {
Object resultInventory = Reflections.field$CraftResultInventory$resultInventory.get(inventory);
Reflections.field$ResultContainer$recipeUsed.set(resultInventory, holderOrRecipe);
} catch (ReflectiveOperationException e) {
plugin.logger().warn("Failed to correct used recipe", e);
}
}
private OptimizedIDItem<ItemStack> getOptimizedIDItem(@Nullable ItemStack itemStack) {
if (ItemUtils.isEmpty(itemStack)) {
return EMPTY;
} else {
Item<ItemStack> wrappedItem = this.itemManager.wrap(itemStack);
Optional<Holder.Reference<Key>> idHolder = BuiltInRegistries.OPTIMIZED_ITEM_ID.get(wrappedItem.id());
return idHolder.map(keyReference -> new OptimizedIDItem<>(keyReference, itemStack)).orElse(EMPTY);
}
}
}

View File

@@ -85,8 +85,6 @@ public class BukkitInjector {
public static void init() {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
clazz$InjectedCacheChecker = byteBuddy
.subclass(Object.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING)
.implement(Reflections.clazz$RecipeManager$CachedCheck)
@@ -349,7 +347,7 @@ public class BukkitInjector {
boolean isCustom = recipeManager.isCustomRecipe(recipeId);
if (!isCustom) {
field$InjectedCacheChecker$lastRecipe.set(thisObj, resourceLocation);
return optionalRecipe;
return Optional.of(pair.getSecond());
}
Item<ItemStack> wrappedItem = BukkitItemManager.instance().wrap(itemStack);

View File

@@ -4125,6 +4125,18 @@ public class Reflections {
)
);
public static final Class<?> clazz$CraftSmithingTransformRecipe = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleCBClass("inventory.CraftSmithingTransformRecipe")
)
);
public static final Method method$CraftSmithingTransformRecipe$fromBukkitRecipe = requireNonNull(
ReflectionUtils.getStaticMethod(
clazz$CraftSmithingTransformRecipe, clazz$CraftSmithingTransformRecipe, SmithingTransformRecipe.class
)
);
public static final Class<?> clazz$FeatureFlagSet = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.flag.FeatureFlagSet")
@@ -4255,6 +4267,12 @@ public class Reflections {
BukkitReflectionUtils.assembleMCClass("world.item.crafting.RecipeHolder")
);
// 1.20.2-1.21.1 resource location
// 1.21.2+ resource key
public static final Constructor<?> constructor$RecipeHolder = Optional.ofNullable(clazz$RecipeHolder)
.map(it -> ReflectionUtils.getConstructor(it, 0))
.orElse(null);
// 1.20.2+
public static final Field field$RecipeHolder$recipe = Optional.ofNullable(clazz$RecipeHolder)
.map(it -> ReflectionUtils.getDeclaredField(it, 1))
@@ -4396,6 +4414,18 @@ public class Reflections {
)
);
public static final Class<?> clazz$CraftResultInventory = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleCBClass("inventory.CraftResultInventory")
)
);
public static final Field field$CraftResultInventory$resultInventory = requireNonNull(
ReflectionUtils.getDeclaredField(
clazz$CraftResultInventory, clazz$Container, 0
)
);
// 1.20.5+
public static final Method method$ItemStack$hurtAndBreak =
ReflectionUtils.getMethod(
@@ -4933,4 +4963,56 @@ public class Reflections {
ReflectionUtils.getDeclaredField(
clazz$CraftInventoryAnvil, clazz$AnvilMenu, 0
);
public static final Class<?> clazz$SmithingTransformRecipe = requireNonNull(
ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("world.item.crafting.SmithingTransformRecipe")
)
);
public static final Constructor<?> constructor$SmithingTransformRecipe = requireNonNull(
VersionHelper.isVersionNewerThan1_21_2() ?
ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, Optional.class, Optional.class, Optional.class, clazz$ItemStack) :
VersionHelper.isVersionNewerThan1_20_2() ?
ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, clazz$Ingredient, clazz$Ingredient, clazz$Ingredient, clazz$ItemStack) :
ReflectionUtils.getConstructor(clazz$SmithingTransformRecipe, clazz$ResourceLocation, clazz$Ingredient, clazz$Ingredient, clazz$Ingredient, clazz$ItemStack)
);
public static final Method method$RecipeManager$addRecipe = requireNonNull(
VersionHelper.isVersionNewerThan1_20_2() ?
ReflectionUtils.getMethod(clazz$RecipeManager, void.class, clazz$RecipeHolder) :
ReflectionUtils.getMethod(clazz$RecipeManager, void.class, clazz$Recipe)
);
public static final Method method$CraftRecipe$toIngredient = requireNonNull(
ReflectionUtils.getStaticMethod(
clazz$CraftRecipe, clazz$Ingredient, RecipeChoice.class, boolean.class
)
);
// 1.20.5+
public static final Method method$ItemStack$transmuteCopy = ReflectionUtils.getMethod(
clazz$ItemStack, clazz$ItemStack, clazz$ItemLike, int.class
);
// 1.20.5+
public static final Class<?> clazz$DataComponentPatch = ReflectionUtils.getClazz(
BukkitReflectionUtils.assembleMCClass("core.component.DataComponentPatch")
);
// 1.20.5+
public static final Method method$ItemStack$getComponentsPatch = Optional.ofNullable(clazz$DataComponentPatch)
.map(it -> ReflectionUtils.getMethod(clazz$ItemStack, it))
.orElse(null);
// 1.20.5+
public static final Method method$ItemStack$applyComponents = Optional.ofNullable(clazz$DataComponentPatch)
.map(it -> ReflectionUtils.getMethod(clazz$ItemStack, void.class, it))
.orElse(null);
public static final Method method$ItemStack$getItem = requireNonNull(
ReflectionUtils.getMethod(
clazz$ItemStack, clazz$Item
)
);
}

View File

@@ -1,5 +1,5 @@
plugins {
id("com.gradleup.shadow") version "9.0.0-beta6"
id("com.gradleup.shadow") version "9.0.0-beta11"
id("maven-publish")
}

View File

@@ -143,6 +143,7 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
this.factory.customName(this.item, displayName);
return this;
}
@Override
public Item<I> itemName(String itemName) {
this.factory.itemName(this.item, itemName);
@@ -276,4 +277,10 @@ public class AbstractItem<W extends ItemWrapper<I>, I> implements Item<I> {
public Object getLiteralObject() {
return this.item.getLiteralObject();
}
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public Item<I> merge(Item<?> another) {
return new AbstractItem<>(this.factory, this.factory.merge(this.item, ((AbstractItem) another).item));
}
}

View File

@@ -116,4 +116,6 @@ public interface Item<I> {
boolean is(Key itemTag);
Object getLiteralObject();
Item<I> merge(Item<?> another);
}

View File

@@ -108,4 +108,6 @@ public abstract class ItemFactory<P extends Plugin, W extends ItemWrapper<I>, I>
protected abstract void repairCost(ItemWrapper<I> item, Integer data);
protected abstract Optional<Integer> repairCost(ItemWrapper<I> item);
protected abstract ItemWrapper<I> merge(ItemWrapper<I> item1, ItemWrapper<I> item2);
}

View File

@@ -1,5 +1,7 @@
package net.momirealms.craftengine.core.item;
import java.util.Map;
public interface ItemWrapper<I> {
I getItem();
@@ -37,4 +39,6 @@ public interface ItemWrapper<I> {
void count(int amount);
ItemWrapper<I> copyWithCount(int count);
Map<String, Object> getData();
}

View File

@@ -4,18 +4,17 @@ import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.Nullable;
public abstract class AbstractRecipe<T> implements Recipe<T> {
public abstract class AbstractGroupedRecipe<T> implements Recipe<T> {
protected final String group;
protected final Key id;
protected final CustomRecipeResult<T> result;
protected AbstractRecipe(Key id, String group, CustomRecipeResult<T> result) {
protected AbstractGroupedRecipe(Key id, String group, CustomRecipeResult<T> result) {
this.group = group;
this.id = id;
this.result = result;
}
@Override
@Nullable
public String group() {
return group;

View File

@@ -21,11 +21,11 @@ public class CustomBlastingRecipe<T> extends CustomCookingRecipe<T> {
return RecipeTypes.BLASTING;
}
public static class Factory<A> implements RecipeFactory<CustomBlastingRecipe<A>> {
public static class Factory<A> implements RecipeFactory<A> {
@SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"})
@Override
public Recipe<CustomBlastingRecipe<A>> create(Key id, Map<String, Object> arguments) {
public Recipe<A> create(Key id, Map<String, Object> arguments) {
CookingRecipeCategory recipeCategory = arguments.containsKey("category") ? CookingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null;
String group = arguments.containsKey("group") ? arguments.get("group").toString() : null;
int cookingTime = MiscUtils.getAsInt(arguments.getOrDefault("time", 80));

View File

@@ -21,11 +21,11 @@ public class CustomCampfireRecipe<T> extends CustomCookingRecipe<T> {
return RecipeTypes.CAMPFIRE_COOKING;
}
public static class Factory<A> implements RecipeFactory<CustomCampfireRecipe<A>> {
public static class Factory<A> implements RecipeFactory<A> {
@SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"})
@Override
public Recipe<CustomCampfireRecipe<A>> create(Key id, Map<String, Object> arguments) {
public Recipe<A> create(Key id, Map<String, Object> arguments) {
CookingRecipeCategory recipeCategory = arguments.containsKey("category") ? CookingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null;
String group = arguments.containsKey("group") ? arguments.get("group").toString() : null;
int cookingTime = MiscUtils.getAsInt(arguments.getOrDefault("time", 80));

View File

@@ -6,7 +6,7 @@ import net.momirealms.craftengine.core.util.Key;
import java.util.List;
public abstract class CustomCookingRecipe<T> extends AbstractRecipe<T> {
public abstract class CustomCookingRecipe<T> extends AbstractGroupedRecipe<T> {
protected final CookingRecipeCategory category;
protected final Ingredient<T> ingredient;
protected final float experience;

View File

@@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.item.recipe;
import net.momirealms.craftengine.core.util.Key;
public abstract class CustomCraftingTableRecipe<T> extends AbstractRecipe<T> {
public abstract class CustomCraftingTableRecipe<T> extends AbstractGroupedRecipe<T> {
protected final CraftingRecipeCategory category;
protected CustomCraftingTableRecipe(Key id, CraftingRecipeCategory category, String group, CustomRecipeResult<T> result) {

View File

@@ -21,11 +21,11 @@ public class CustomSmeltingRecipe<T> extends CustomCookingRecipe<T> {
return RecipeTypes.SMELTING;
}
public static class Factory<A> implements RecipeFactory<CustomSmeltingRecipe<A>> {
public static class Factory<A> implements RecipeFactory<A> {
@SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"})
@Override
public Recipe<CustomSmeltingRecipe<A>> create(Key id, Map<String, Object> arguments) {
public Recipe<A> create(Key id, Map<String, Object> arguments) {
CookingRecipeCategory recipeCategory = arguments.containsKey("category") ? CookingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null;
String group = arguments.containsKey("group") ? arguments.get("group").toString() : null;
int cookingTime = MiscUtils.getAsInt(arguments.getOrDefault("time", 80));

View File

@@ -0,0 +1,200 @@
package net.momirealms.craftengine.core.item.recipe;
import net.momirealms.craftengine.core.item.Item;
import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.item.recipe.input.RecipeInput;
import net.momirealms.craftengine.core.item.recipe.input.SmithingInput;
import net.momirealms.craftengine.core.plugin.CraftEngine;
import net.momirealms.craftengine.core.registry.BuiltInRegistries;
import net.momirealms.craftengine.core.registry.Holder;
import net.momirealms.craftengine.core.registry.Registries;
import net.momirealms.craftengine.core.registry.WritableRegistry;
import net.momirealms.craftengine.core.util.Key;
import net.momirealms.craftengine.core.util.MiscUtils;
import net.momirealms.craftengine.core.util.ResourceKey;
import net.momirealms.craftengine.core.util.TriConsumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class CustomSmithingTransformRecipe<T> implements Recipe<T> {
public static final Factory<?> FACTORY = new Factory<>();
private final Key id;
private final CustomRecipeResult<T> result;
private final Ingredient<T> base;
private final Ingredient<T> template;
private final Ingredient<T> addition;
private final boolean mergeComponents;
private final List<ItemDataProcessor> processors;
public CustomSmithingTransformRecipe(Key id,
@Nullable Ingredient<T> base,
@Nullable Ingredient<T> template,
@Nullable Ingredient<T> addition,
CustomRecipeResult<T> result,
boolean mergeComponents,
List<ItemDataProcessor> processors
) {
this.id = id;
this.result = result;
this.base = base;
this.template = template;
this.addition = addition;
this.processors = processors;
this.mergeComponents = mergeComponents;
}
@SuppressWarnings("unchecked")
@Override
public boolean matches(RecipeInput input) {
SmithingInput<T> smithingInput = (SmithingInput<T>) input;
return checkIngredient(this.base, smithingInput.base())
&& checkIngredient(this.template, smithingInput.template())
&& checkIngredient(this.addition, smithingInput.addition());
}
private boolean checkIngredient(Ingredient<T> ingredient, OptimizedIDItem<T> item) {
if (ingredient != null) {
if (item == null || item.isEmpty()) {
return false;
}
return ingredient.test(item);
} else {
return item == null || item.isEmpty();
}
}
@Override
public List<Ingredient<T>> ingredientsInUse() {
List<Ingredient<T>> ingredients = new ArrayList<>();
ingredients.add(this.base);
if (this.template != null) {
ingredients.add(this.template);
}
if (this.addition != null) {
ingredients.add(this.addition);
}
return ingredients;
}
@Override
public @NotNull Key type() {
return RecipeTypes.SMITHING_TRANSFORM;
}
@Override
public Key id() {
return this.id;
}
@Override
public T result(ItemBuildContext context) {
return this.result.buildItemStack(context);
}
@SuppressWarnings("unchecked")
public T assemble(ItemBuildContext context, Item<T> base) {
T result = this.result(context);
Item<T> wrappedResult = (Item<T>) CraftEngine.instance().itemManager().wrap(result);
Item<T> finalResult = wrappedResult;
if (this.mergeComponents) {
finalResult = base.merge(wrappedResult);
}
for (ItemDataProcessor processor : this.processors) {
processor.accept(base, wrappedResult, finalResult);
}
return finalResult.load();
}
@Override
public CustomRecipeResult<T> result() {
return this.result;
}
@Nullable
public Ingredient<T> base() {
return this.base;
}
@Nullable
public Ingredient<T> template() {
return template;
}
@Nullable
public Ingredient<T> addition() {
return addition;
}
@SuppressWarnings({"DuplicatedCode"})
public static class Factory<A> implements RecipeFactory<A> {
@Override
public Recipe<A> create(Key id, Map<String, Object> arguments) {
List<String> base = MiscUtils.getAsStringList(arguments.get("base"));
List<String> addition = MiscUtils.getAsStringList(arguments.get("addition"));
List<String> template = MiscUtils.getAsStringList(arguments.get("template-type"));
boolean mergeComponents = (boolean) arguments.getOrDefault("merge-components", true);
@SuppressWarnings("unchecked")
List<Map<String, Object>> processors = (List<Map<String, Object>>) arguments.getOrDefault("post-processors", List.of());
return new CustomSmithingTransformRecipe<>(
id,
toIngredient(base), toIngredient(template),toIngredient(addition), parseResult(arguments),
mergeComponents,
ItemDataProcessors.fromMapList(processors)
);
}
private Ingredient<A> toIngredient(List<String> items) {
Set<Holder<Key>> holders = new HashSet<>();
for (String item : items) {
if (item.charAt(0) == '#') {
holders.addAll(CraftEngine.instance().itemManager().tagToItems(Key.of(item.substring(1))));
} else {
holders.add(BuiltInRegistries.OPTIMIZED_ITEM_ID.get(Key.of(item)).orElseThrow(() -> new IllegalArgumentException("Invalid vanilla/custom item: " + item)));
}
}
return holders.isEmpty() ? null : Ingredient.of(holders);
}
}
public static class ItemDataProcessors {
public static List<ItemDataProcessor> fromMapList(List<Map<String, Object>> mapList) {
if (mapList == null || mapList.isEmpty()) return List.of();
List<ItemDataProcessor> functions = new ArrayList<>();
for (Map<String, Object> map : mapList) {
functions.add(fromMap(map));
}
return functions;
}
public static ItemDataProcessor fromMap(Map<String, Object> map) {
String type = (String) map.get("type");
if (type == null) {
throw new NullPointerException("processor type cannot be null");
}
Key key = Key.withDefaultNamespace(type, "craftengine");
ItemDataProcessor.Factory factory = BuiltInRegistries.SMITHING_RESULT_PROCESSOR_FACTORY.getValue(key);
if (factory == null) {
throw new IllegalArgumentException("Unknown processor type: " + type);
}
return factory.create(map);
}
public static void register(Key key, ItemDataProcessor.Factory factory) {
Holder.Reference<ItemDataProcessor.Factory> holder = ((WritableRegistry<ItemDataProcessor.Factory>) BuiltInRegistries.SMITHING_RESULT_PROCESSOR_FACTORY)
.registerForHolder(new ResourceKey<>(Registries.SMITHING_RESULT_PROCESSOR_FACTORY.location(), key));
holder.bindValue(factory);
}
}
@FunctionalInterface
public interface ItemDataProcessor extends TriConsumer<Item<?>, Item<?>, Item<?>> {
interface Factory {
ItemDataProcessor create(Map<String, Object> arguments);
}
}
}

View File

@@ -21,11 +21,11 @@ public class CustomSmokingRecipe<T> extends CustomCookingRecipe<T> {
return RecipeTypes.SMOKING;
}
public static class Factory<A> implements RecipeFactory<CustomSmokingRecipe<A>> {
public static class Factory<A> implements RecipeFactory<A> {
@SuppressWarnings({"unchecked", "rawtypes", "DuplicatedCode"})
@Override
public Recipe<CustomSmokingRecipe<A>> create(Key id, Map<String, Object> arguments) {
public Recipe<A> create(Key id, Map<String, Object> arguments) {
CookingRecipeCategory recipeCategory = arguments.containsKey("category") ? CookingRecipeCategory.valueOf(arguments.get("category").toString().toUpperCase(Locale.ENGLISH)) : null;
String group = arguments.containsKey("group") ? arguments.get("group").toString() : null;
int cookingTime = MiscUtils.getAsInt(arguments.getOrDefault("time", 80));

View File

@@ -14,7 +14,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
public class CustomStoneCuttingRecipe<T> extends AbstractRecipe<T> {
public class CustomStoneCuttingRecipe<T> extends AbstractGroupedRecipe<T> {
public static final Factory<?> FACTORY = new Factory<>();
protected final Ingredient<T> ingredient;
@@ -36,7 +36,7 @@ public class CustomStoneCuttingRecipe<T> extends AbstractRecipe<T> {
@Override
public @NotNull Key type() {
return RecipeTypes.STONE_CUTTING;
return RecipeTypes.STONECUTTING;
}
public Ingredient<T> ingredient() {

View File

@@ -4,7 +4,6 @@ import net.momirealms.craftengine.core.item.ItemBuildContext;
import net.momirealms.craftengine.core.item.recipe.input.RecipeInput;
import net.momirealms.craftengine.core.util.Key;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@@ -22,7 +21,4 @@ public interface Recipe<T> {
Key type();
Key id();
@Nullable
String group();
}

View File

@@ -16,7 +16,9 @@ public class RecipeTypes {
public static final Key BLASTING = Key.of("minecraft:blasting");
public static final Key SMOKING = Key.of("minecraft:smoking");
public static final Key CAMPFIRE_COOKING = Key.of("minecraft:campfire_cooking");
public static final Key STONE_CUTTING = Key.of("minecraft:stone_cutting");
public static final Key STONECUTTING = Key.of("minecraft:stonecutting");
public static final Key SMITHING_TRANSFORM = Key.of("minecraft:smithing_transform");
public static final Key SMITHING_TRIM = Key.of("minecraft:smithing_trim");
static {
register(SHAPED, CustomShapedRecipe.FACTORY);
@@ -25,7 +27,8 @@ public class RecipeTypes {
register(SMOKING, CustomSmokingRecipe.FACTORY);
register(BLASTING, CustomBlastingRecipe.FACTORY);
register(CAMPFIRE_COOKING, CustomCampfireRecipe.FACTORY);
register(STONE_CUTTING, CustomStoneCuttingRecipe.FACTORY);
register(STONECUTTING, CustomStoneCuttingRecipe.FACTORY);
register(SMITHING_TRANSFORM, CustomSmithingTransformRecipe.FACTORY);
}
public static <T> void register(Key key, RecipeFactory<T> factory) {

View File

@@ -0,0 +1,33 @@
package net.momirealms.craftengine.core.item.recipe.input;
import net.momirealms.craftengine.core.item.recipe.OptimizedIDItem;
import org.jetbrains.annotations.Nullable;
public class SmithingInput<T> implements RecipeInput {
private final OptimizedIDItem<T> base;
private final OptimizedIDItem<T> template;
private final OptimizedIDItem<T> addition;
public SmithingInput(@Nullable OptimizedIDItem<T> base,
@Nullable OptimizedIDItem<T> template,
@Nullable OptimizedIDItem<T> addition) {
this.base = base;
this.template = template;
this.addition = addition;
}
@Nullable
public OptimizedIDItem<T> base() {
return base;
}
@Nullable
public OptimizedIDItem<T> template() {
return template;
}
@Nullable
public OptimizedIDItem<T> addition() {
return addition;
}
}

View File

@@ -1,6 +1,8 @@
package net.momirealms.craftengine.core.item.recipe.vanilla;
import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory;
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
import net.momirealms.craftengine.core.util.Key;
import java.util.List;
@@ -9,4 +11,9 @@ public class VanillaBlastingRecipe extends VanillaCookingRecipe {
public VanillaBlastingRecipe(CookingRecipeCategory category, String group, RecipeResult result, List<String> ingredient, float experience, int cookingTime) {
super(category, group, result, ingredient, experience, cookingTime);
}
@Override
public Key type() {
return RecipeTypes.BLASTING;
}
}

View File

@@ -1,6 +1,8 @@
package net.momirealms.craftengine.core.item.recipe.vanilla;
import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory;
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
import net.momirealms.craftengine.core.util.Key;
import java.util.List;
@@ -9,4 +11,9 @@ public class VanillaCampfireRecipe extends VanillaCookingRecipe {
public VanillaCampfireRecipe(CookingRecipeCategory category, String group, RecipeResult result, List<String> ingredient, float experience, int cookingTime) {
super(category, group, result, ingredient, experience, cookingTime);
}
@Override
public Key type() {
return RecipeTypes.CAMPFIRE_COOKING;
}
}

View File

@@ -4,7 +4,7 @@ import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory;
import java.util.List;
public abstract class VanillaCookingRecipe extends VanillaRecipe {
public abstract class VanillaCookingRecipe extends VanillaGroupedRecipe {
protected final List<String> ingredient;
protected final CookingRecipeCategory category;
protected final float experience;

View File

@@ -2,7 +2,7 @@ package net.momirealms.craftengine.core.item.recipe.vanilla;
import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory;
public class VanillaCraftingRecipe extends VanillaRecipe {
public abstract class VanillaCraftingRecipe extends VanillaGroupedRecipe {
protected final CraftingRecipeCategory category;
protected VanillaCraftingRecipe(CraftingRecipeCategory category, String group, RecipeResult result) {

View File

@@ -0,0 +1,20 @@
package net.momirealms.craftengine.core.item.recipe.vanilla;
public abstract class VanillaGroupedRecipe implements VanillaRecipe {
protected final String group;
protected final RecipeResult result;
protected VanillaGroupedRecipe(String group, RecipeResult result) {
this.group = group;
this.result = result;
}
public String group() {
return group;
}
@Override
public RecipeResult result() {
return result;
}
}

View File

@@ -1,19 +1,10 @@
package net.momirealms.craftengine.core.item.recipe.vanilla;
public abstract class VanillaRecipe {
protected final String group;
protected final RecipeResult result;
import net.momirealms.craftengine.core.util.Key;
protected VanillaRecipe(String group, RecipeResult result) {
this.group = group;
this.result = result;
}
public interface VanillaRecipe {
public String group() {
return group;
}
Key type();
public RecipeResult result() {
return result;
}
RecipeResult result();
}

View File

@@ -17,4 +17,6 @@ public interface VanillaRecipeReader {
VanillaCampfireRecipe readCampfire(JsonObject json);
VanillaStoneCuttingRecipe readStoneCutting(JsonObject json);
VanillaSmithingTransformRecipe readSmithingTransform(JsonObject json);
}

View File

@@ -1,6 +1,8 @@
package net.momirealms.craftengine.core.item.recipe.vanilla;
import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory;
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
import net.momirealms.craftengine.core.util.Key;
import java.util.List;
import java.util.Map;
@@ -26,4 +28,9 @@ public class VanillaShapedRecipe extends VanillaCraftingRecipe {
public String[] pattern() {
return pattern;
}
@Override
public Key type() {
return RecipeTypes.SHAPED;
}
}

View File

@@ -1,6 +1,8 @@
package net.momirealms.craftengine.core.item.recipe.vanilla;
import net.momirealms.craftengine.core.item.recipe.CraftingRecipeCategory;
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
import net.momirealms.craftengine.core.util.Key;
import java.util.List;
@@ -15,4 +17,9 @@ public class VanillaShapelessRecipe extends VanillaCraftingRecipe {
public List<List<String>> ingredients() {
return ingredients;
}
@Override
public Key type() {
return RecipeTypes.SHAPELESS;
}
}

View File

@@ -1,6 +1,8 @@
package net.momirealms.craftengine.core.item.recipe.vanilla;
import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory;
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
import net.momirealms.craftengine.core.util.Key;
import java.util.List;
@@ -9,4 +11,9 @@ public class VanillaSmeltingRecipe extends VanillaCookingRecipe {
public VanillaSmeltingRecipe(CookingRecipeCategory category, String group, RecipeResult result, List<String> ingredient, float experience, int cookingTime) {
super(category, group, result, ingredient, experience, cookingTime);
}
@Override
public Key type() {
return RecipeTypes.SMELTING;
}
}

View File

@@ -0,0 +1,42 @@
package net.momirealms.craftengine.core.item.recipe.vanilla;
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
import net.momirealms.craftengine.core.util.Key;
import java.util.List;
public class VanillaSmithingTransformRecipe implements VanillaRecipe {
private final RecipeResult result;
private final List<String> base;
private final List<String> template;
private final List<String> addition;
public VanillaSmithingTransformRecipe(List<String> base, List<String> template, List<String> addition, RecipeResult result) {
this.result = result;
this.base = base;
this.template = template;
this.addition = addition;
}
@Override
public Key type() {
return RecipeTypes.SMITHING_TRANSFORM;
}
@Override
public RecipeResult result() {
return result;
}
public List<String> base() {
return base;
}
public List<String> template() {
return template;
}
public List<String> addition() {
return addition;
}
}

View File

@@ -1,6 +1,8 @@
package net.momirealms.craftengine.core.item.recipe.vanilla;
import net.momirealms.craftengine.core.item.recipe.CookingRecipeCategory;
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
import net.momirealms.craftengine.core.util.Key;
import java.util.List;
@@ -9,4 +11,9 @@ public class VanillaSmokingRecipe extends VanillaCookingRecipe {
public VanillaSmokingRecipe(CookingRecipeCategory category, String group, RecipeResult result, List<String> ingredient, float experience, int cookingTime) {
super(category, group, result, ingredient, experience, cookingTime);
}
@Override
public Key type() {
return RecipeTypes.SMOKING;
}
}

View File

@@ -1,8 +1,11 @@
package net.momirealms.craftengine.core.item.recipe.vanilla;
import net.momirealms.craftengine.core.item.recipe.RecipeTypes;
import net.momirealms.craftengine.core.util.Key;
import java.util.List;
public class VanillaStoneCuttingRecipe extends VanillaRecipe {
public class VanillaStoneCuttingRecipe extends VanillaGroupedRecipe {
private final List<String> ingredient;
public VanillaStoneCuttingRecipe(String group, RecipeResult result, List<String> ingredient) {
@@ -13,4 +16,9 @@ public class VanillaStoneCuttingRecipe extends VanillaRecipe {
public List<String> ingredient() {
return ingredient;
}
@Override
public Key type() {
return RecipeTypes.STONECUTTING;
}
}

View File

@@ -91,6 +91,16 @@ public class VanillaRecipeReader1_20 extends AbstractRecipeReader {
);
}
@Override
public VanillaSmithingTransformRecipe readSmithingTransform(JsonObject json) {
return new VanillaSmithingTransformRecipe(
readSingleIngredient(json.get("base")),
readSingleIngredient(json.get("template")),
readSingleIngredient(json.get("addition")),
readSmithingResult(json.getAsJsonObject("result"))
);
}
protected List<String> readSingleIngredient(JsonElement json) {
List<String> ingredients = new ArrayList<>();
if (json.isJsonObject()) {
@@ -126,6 +136,12 @@ public class VanillaRecipeReader1_20 extends AbstractRecipeReader {
return new RecipeResult(item, count, null);
}
@NotNull
protected RecipeResult readSmithingResult(JsonObject object) {
String item = object.get("item").getAsString();
return new RecipeResult(item, 1, null);
}
protected List<List<String>> readShapelessIngredients(JsonArray json) {
List<List<String>> ingredients = new ArrayList<>();
for (JsonElement element : json) {

View File

@@ -26,4 +26,9 @@ public class VanillaRecipeReader1_20_5 extends VanillaRecipeReader1_20 {
protected RecipeResult readStoneCuttingResult(JsonObject json) {
return readCraftingResult(json.getAsJsonObject("result"));
}
@Override
protected @NotNull RecipeResult readSmithingResult(JsonObject object) {
return readCraftingResult(object.getAsJsonObject());
}
}

View File

@@ -26,7 +26,7 @@ public class NumberProviders {
holder.bindValue(factory);
}
static List<NumberProvider> fromMapList(List<Map<String, Object>> mapList) {
public static List<NumberProvider> fromMapList(List<Map<String, Object>> mapList) {
if (mapList == null || mapList.isEmpty()) return List.of();
List<NumberProvider> functions = new ArrayList<>();
for (Map<String, Object> map : mapList) {

View File

@@ -249,6 +249,7 @@ public abstract class AbstractPackManager implements PackManager {
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/smelting.png");
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/campfire.png");
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/stonecutting_recipe.png");
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/smithing_transform_recipe.png");
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/cooking_recipe.png");
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/crafting_recipe.png");
plugin.saveResource("resources/internal/resourcepack/assets/minecraft/textures/font/gui/custom/no_recipe.png");

View File

@@ -54,6 +54,7 @@ public class ConfigManager implements Reloadable {
protected long resource_pack$protection$obfuscation$seed;
protected boolean resource_pack$protection$obfuscation$fake_directory;
protected boolean resource_pack$protection$obfuscation$escape_unicode;
protected boolean resource_pack$protection$obfuscation$break_json;
protected boolean resource_pack$protection$obfuscation$resource_location$enable;
protected int resource_pack$protection$obfuscation$resource_location$random_namespace$length;
protected int resource_pack$protection$obfuscation$resource_location$random_namespace$amount;
@@ -200,6 +201,7 @@ public class ConfigManager implements Reloadable {
resource_pack$protection$obfuscation$seed = config.getLong("resource-pack.protection.obfuscation.seed", 0L);
resource_pack$protection$obfuscation$fake_directory = config.getBoolean("resource-pack.protection.obfuscation.fake-directory", false);
resource_pack$protection$obfuscation$escape_unicode = config.getBoolean("resource-pack.protection.obfuscation.escape-unicode", false);
resource_pack$protection$obfuscation$break_json = config.getBoolean("resource-pack.protection.obfuscation.break-json", false);
resource_pack$protection$obfuscation$resource_location$enable = config.getBoolean("resource-pack.protection.obfuscation.resource-location.enable", false);
resource_pack$protection$obfuscation$resource_location$random_namespace$amount = config.getInt("resource-pack.protection.obfuscation.resource-location.random-namespace.amount", 32);
@@ -435,6 +437,10 @@ public class ConfigManager implements Reloadable {
return instance.resource_pack$protection$obfuscation$escape_unicode;
}
public static boolean breakJson() {
return instance.resource_pack$protection$obfuscation$break_json;
}
public static boolean enableRandomResourceLocation() {
return instance.resource_pack$protection$obfuscation$resource_location$enable;
}

View File

@@ -65,6 +65,7 @@ public interface ItemBrowserManager extends Reloadable, ConfigSectionParser {
public static String RECIPE_CAMPFIRE_TITLE;
public static String RECIPE_CRAFTING_TITLE;
public static String RECIPE_STONECUTTING_TITLE;
public static String RECIPE_SMITHING_TRANSFORM_TITLE;
public static Key RECIPE_BACK;
public static Key RECIPE_EXIT;
public static Key RECIPE_NEXT_PAGE_AVAILABLE;
@@ -100,9 +101,10 @@ public interface ItemBrowserManager extends Reloadable, ConfigSectionParser {
RECIPE_BLASTING_TITLE = getOrThrow(section, "recipe.blasting.title");
RECIPE_SMELTING_TITLE = getOrThrow(section, "recipe.smelting.title");
RECIPE_SMOKING_TITLE = getOrThrow(section, "recipe.smoking.title");
RECIPE_CAMPFIRE_TITLE = getOrThrow(section, "recipe.campfire.title");
RECIPE_CAMPFIRE_TITLE = getOrThrow(section, "recipe.campfire-cooking.title");
RECIPE_CRAFTING_TITLE = getOrThrow(section, "recipe.crafting.title");
RECIPE_STONECUTTING_TITLE = getOrThrow(section, "recipe.stonecutting.title");
RECIPE_SMITHING_TRANSFORM_TITLE = getOrThrow(section, "recipe.smithing-transform.title");
RECIPE_BACK = Key.of(getOrThrow(section, "recipe.page-navigation.return"));
RECIPE_EXIT = Key.of(getOrThrow(section, "recipe.page-navigation.exit"));
RECIPE_NEXT_PAGE_AVAILABLE = Key.of(getOrThrow(section, "recipe.page-navigation.next.available"));

View File

@@ -349,10 +349,215 @@ public class ItemBrowserManagerImpl implements ItemBrowserManager {
openCookingRecipePage(player, (CustomCookingRecipe<Object>) recipe, parentGui, recipes, index, depth, canOpenNoRecipePage);
return;
}
if (recipeType == RecipeTypes.STONE_CUTTING) {
if (recipeType == RecipeTypes.STONECUTTING) {
openStoneCuttingRecipePage(player, (CustomStoneCuttingRecipe<Object>) recipe, parentGui, recipes, index, depth, canOpenNoRecipePage);
return;
}
if (recipeType == RecipeTypes.SMITHING_TRANSFORM) {
openSmithingTransformRecipePage(player, (CustomSmithingTransformRecipe<Object>) recipe, parentGui, recipes, index, depth, canOpenNoRecipePage);
return;
}
}
public void openSmithingTransformRecipePage(Player player, CustomSmithingTransformRecipe<Object> recipe, Gui parentGui, List<Recipe<Object>> recipes, int index, int depth, boolean canOpenNoRecipePage) {
Key previous = index > 0 ? Constants.RECIPE_PREVIOUS_PAGE_AVAILABLE : Constants.RECIPE_PREVIOUS_PAGE_BLOCK;
Key next = index + 1 < recipes.size() ? Constants.RECIPE_NEXT_PAGE_AVAILABLE : Constants.RECIPE_NEXT_PAGE_BLOCK;
Key result = recipe.result().item().id();
GuiLayout layout = new GuiLayout(
" ",
" ",
" ABC X ",
" ^ ",
" ",
" < = > "
)
.addIngredient('X', GuiElement.constant(this.plugin.itemManager().createWrappedItem(result, player).count(recipe.result().count()), (e, c) -> {
c.cancel();
if (MIDDLE_CLICK.contains(c.type()) && player.isCreativeMode() && player.hasPermission(GET_ITEM_PERMISSION) && c.itemOnCursor() == null) {
Item<?> item = this.plugin.itemManager().createWrappedItem(result, player);
item.count(item.maxStackSize());
c.setItemOnCursor(item);
return;
}
if (LEFT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByResult(result);
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
} else if (canOpenNoRecipePage) {
openNoRecipePage(player, result, e.gui(), 0);
}
} else if (RIGHT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByIngredient(result);
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
}
}
}))
.addIngredient('^', player.hasPermission(GET_ITEM_PERMISSION) ? GuiElement.constant(this.plugin.itemManager().createWrappedItem(Constants.RECIPE_GET_ITEM, player), (e, c) -> {
c.cancel();
player.playSound(Constants.SOUND_PICK_ITEM);
if (LEFT_CLICK.contains(c.type())) {
player.giveItem(this.plugin.itemManager().createWrappedItem(result, player));
} else if (RIGHT_CLICK.contains(c.type())) {
Item<?> item = this.plugin.itemManager().createWrappedItem(result, player);
player.giveItem(item.count(item.maxStackSize()));
}
}) : GuiElement.EMPTY)
.addIngredient('=', GuiElement.constant(this.plugin.itemManager().getCustomItem(parentGui != null ? Constants.RECIPE_BACK : Constants.RECIPE_EXIT)
.map(it -> it.buildItem(ItemBuildContext.of(player, ContextHolder.EMPTY)))
.orElseThrow(() -> new GuiElementMissingException("Can't find gui element " + (parentGui != null ? Constants.RECIPE_BACK : Constants.RECIPE_EXIT))),
((element, click) -> {
click.cancel();
player.playSound(Constants.SOUND_RETURN_PAGE, 0.25f, 1);
if (parentGui != null) {
parentGui.open(player);
} else {
player.closeInventory();
}
}))
)
.addIngredient('>', GuiElement.constant(this.plugin.itemManager()
.getCustomItem(next)
.map(it -> it.buildItem(ItemBuildContext.of(player, ContextHolder.builder()
.withParameter(GuiParameters.CURRENT_PAGE, String.valueOf(index + 1))
.withParameter(GuiParameters.MAX_PAGE, String.valueOf(recipes.size()))
.build())))
.orElseThrow(() -> new GuiElementMissingException("Can't find gui element " + next)), (e, c) -> {
c.cancel();
if (index + 1 < recipes.size()) {
player.playSound(Constants.SOUND_CHANGE_PAGE, 0.25f, 1);
openRecipePage(player, parentGui, recipes, index + 1, depth, canOpenNoRecipePage);
}
}))
.addIngredient('<', GuiElement.constant(this.plugin.itemManager()
.getCustomItem(previous)
.map(it -> it.buildItem(ItemBuildContext.of(player, ContextHolder.builder()
.withParameter(GuiParameters.CURRENT_PAGE, String.valueOf(index + 1))
.withParameter(GuiParameters.MAX_PAGE, String.valueOf(recipes.size()))
.build())))
.orElseThrow(() -> new GuiElementMissingException("Can't find gui element " + previous)), (e, c) -> {
c.cancel();
if (index > 0) {
player.playSound(Constants.SOUND_CHANGE_PAGE, 0.25f, 1);
openRecipePage(player, parentGui, recipes, index - 1, depth, canOpenNoRecipePage);
}
}));
List<Item<?>> templates = new ArrayList<>();
Optional.ofNullable(recipe.template()).ifPresent(it -> {
for (Holder<Key> in : it.items()) {
templates.add(this.plugin.itemManager().createWrappedItem(in.value(), player));
}
});
layout.addIngredient('A', templates.isEmpty() ? GuiElement.EMPTY : GuiElement.recipeIngredient(templates, (e, c) -> {
c.cancel();
if (MIDDLE_CLICK.contains(c.type()) && player.isCreativeMode() && player.hasPermission(GET_ITEM_PERMISSION) && c.itemOnCursor() == null) {
Item<?> item = this.plugin.itemManager().createWrappedItem(e.item().id(), player);
item.count(item.maxStackSize());
c.setItemOnCursor(item);
return;
}
if (LEFT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByResult(e.item().id());
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
} else if (canOpenNoRecipePage) {
openNoRecipePage(player, e.item().id(), e.gui(), 0);
}
} else if (RIGHT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByIngredient(e.item().id());
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
}
}
}));
List<Item<?>> bases = new ArrayList<>();
Optional.ofNullable(recipe.base()).ifPresent(it -> {
for (Holder<Key> in : it.items()) {
bases.add(this.plugin.itemManager().createWrappedItem(in.value(), player));
}
});
layout.addIngredient('B', bases.isEmpty() ? GuiElement.EMPTY : GuiElement.recipeIngredient(bases, (e, c) -> {
c.cancel();
if (MIDDLE_CLICK.contains(c.type()) && player.isCreativeMode() && player.hasPermission(GET_ITEM_PERMISSION) && c.itemOnCursor() == null) {
Item<?> item = this.plugin.itemManager().createWrappedItem(e.item().id(), player);
item.count(item.maxStackSize());
c.setItemOnCursor(item);
return;
}
if (LEFT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByResult(e.item().id());
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
} else if (canOpenNoRecipePage) {
openNoRecipePage(player, e.item().id(), e.gui(), 0);
}
} else if (RIGHT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByIngredient(e.item().id());
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
}
}
}));
List<Item<?>> additions = new ArrayList<>();
Optional.ofNullable(recipe.addition()).ifPresent(it -> {
for (Holder<Key> in : it.items()) {
additions.add(this.plugin.itemManager().createWrappedItem(in.value(), player));
}
});
layout.addIngredient('C', additions.isEmpty() ? GuiElement.EMPTY : GuiElement.recipeIngredient(additions, (e, c) -> {
c.cancel();
if (MIDDLE_CLICK.contains(c.type()) && player.isCreativeMode() && player.hasPermission(GET_ITEM_PERMISSION) && c.itemOnCursor() == null) {
Item<?> item = this.plugin.itemManager().createWrappedItem(e.item().id(), player);
item.count(item.maxStackSize());
c.setItemOnCursor(item);
return;
}
if (LEFT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByResult(e.item().id());
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
} else if (canOpenNoRecipePage) {
openNoRecipePage(player, e.item().id(), e.gui(), 0);
}
} else if (RIGHT_CLICK.contains(c.type())) {
List<Recipe<Object>> inRecipes = this.plugin.recipeManager().getRecipeByIngredient(e.item().id());
if (inRecipes == recipes) return;
player.playSound(Constants.SOUND_CLICK_BUTTON);
if (!inRecipes.isEmpty()) {
openRecipePage(c.clicker(), e.gui(), inRecipes, 0, 0, canOpenNoRecipePage);
}
}
}));
BasicGui.builder()
.layout(layout)
.inventoryClickConsumer(c -> {
if (MOVE_TO_OTHER_INV.contains(c.type()) || DOUBLE_CLICK.contains(c.type())) {
c.cancel();
}
})
.build()
.title(AdventureHelper.miniMessage().deserialize(Constants.RECIPE_SMITHING_TRANSFORM_TITLE, ItemBuildContext.of(player, ContextHolder.EMPTY).tagResolvers()))
.refresh()
.open(player);
}
public void openStoneCuttingRecipePage(Player player, CustomStoneCuttingRecipe<Object> recipe, Gui parentGui, List<Recipe<Object>> recipes, int index, int depth, boolean canOpenNoRecipePage) {

View File

@@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.PropertyFactory;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.recipe.CustomSmithingTransformRecipe;
import net.momirealms.craftengine.core.item.recipe.RecipeFactory;
import net.momirealms.craftengine.core.loot.condition.LootConditionFactory;
import net.momirealms.craftengine.core.loot.entry.LootEntryContainerFactory;
@@ -43,6 +44,7 @@ public class BuiltInRegistries {
public static final Registry<ApplyBonusCountFunction.FormulaFactory> FORMULA_FACTORY = createRegistry(Registries.FORMULA_FACTORY);
public static final Registry<PathMatcherFactory> PATH_MATCHER_FACTORY = createRegistry(Registries.PATH_MATCHER_FACTORY);
public static final Registry<ResolutionFactory> RESOLUTION_FACTORY = createRegistry(Registries.RESOLUTION_FACTORY);
public static final Registry<CustomSmithingTransformRecipe.ItemDataProcessor.Factory> SMITHING_RESULT_PROCESSOR_FACTORY = createRegistry(Registries.SMITHING_RESULT_PROCESSOR_FACTORY);
private static <T> Registry<T> createRegistry(ResourceKey<? extends Registry<T>> key) {
return new MappedRegistry<>(key);

View File

@@ -4,6 +4,7 @@ import net.momirealms.craftengine.core.block.CustomBlock;
import net.momirealms.craftengine.core.block.behavior.BlockBehaviorFactory;
import net.momirealms.craftengine.core.block.properties.PropertyFactory;
import net.momirealms.craftengine.core.item.behavior.ItemBehaviorFactory;
import net.momirealms.craftengine.core.item.recipe.CustomSmithingTransformRecipe;
import net.momirealms.craftengine.core.item.recipe.RecipeFactory;
import net.momirealms.craftengine.core.loot.condition.LootConditionFactory;
import net.momirealms.craftengine.core.loot.entry.LootEntryContainerFactory;
@@ -44,4 +45,5 @@ public class Registries {
public static final ResourceKey<Registry<ApplyBonusCountFunction.FormulaFactory>> FORMULA_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("formula_factory"));
public static final ResourceKey<Registry<PathMatcherFactory>> PATH_MATCHER_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("path_matcher_factory"));
public static final ResourceKey<Registry<ResolutionFactory>> RESOLUTION_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("resolution_factory"));
public static final ResourceKey<Registry<CustomSmithingTransformRecipe.ItemDataProcessor.Factory>> SMITHING_RESULT_PROCESSOR_FACTORY = new ResourceKey<>(ROOT_REGISTRY, Key.withDefaultNamespace("smithing_result_processor_factory"));
}

View File

@@ -1,7 +1,7 @@
# Project settings
# Rule: [major update].[feature update].[bug fix]
project_version=0.0.34
config_version=14
project_version=0.0.35
config_version=15
lang_version=3
project_group=net.momirealms
latest_minecraft_version=1.21.4
@@ -38,7 +38,7 @@ geantyref_version=1.3.16
zstd_version=1.5.6-9
commons_io_version=2.17.0
sparrow_nbt_version=0.3
sparrow_util_version=0.22
sparrow_util_version=0.29
fastutil_version=8.5.15
netty_version=4.1.119.Final
joml_version=1.10.8

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -1,7 +1,7 @@
plugins {
id("java-library")
id("com.gradleup.shadow") version "9.0.0-beta6"
id("io.papermc.paperweight.userdev") version "2.0.0-beta.14"
id("com.gradleup.shadow") version "9.0.0-beta11"
id("io.papermc.paperweight.userdev") version "2.0.0-beta.16"
}
repositories {

View File

@@ -9,5 +9,9 @@ pluginManagement {
plugins {
kotlin("jvm") version "2.0.20"
}
repositories {
gradlePluginPortal()
maven("https://repo.papermc.io/repository/maven-public/")
}
}