建立gitignore

This commit is contained in:
1820982382
2026-08-08 02:52:39 +08:00
parent d1ab855948
commit 0c72822f40
22 changed files with 2286 additions and 0 deletions
@@ -0,0 +1,20 @@
package tckubejsbridge;
import net.neoforged.fml.common.Mod;
/**
* TC KubeJS Bridge — 将神秘时代4(Thaumcraft 4 移植版)的配方系统桥接到 KubeJS。
*
* 提供 KubeJS 脚本可调用的:
* - event.recipes.thaumcraft.infusion(...) 注魔配方
* - event.recipes.thaumcraft.crucible(...) 坩埚炼金配方
* - event.recipes.thaumcraft.arcane(...) 奥术有序合成配方
* - event.recipes.thaumcraft.arcane_shapeless(...) 奥术无序合成配方
* - event.thaumcraft.addObjectTag(...) 物品固有要素
*
* 实际注册逻辑在 {@link tckubejsbridge.kubejs.TCKubeJSPlugin}(KubeJS 插件)。
*/
@Mod(TCKubeJSBridge.MODID)
public final class TCKubeJSBridge {
public static final String MODID = "tckubejsbridge";
}
@@ -0,0 +1,179 @@
package tckubejsbridge.kubejs;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import dev.latvian.mods.kubejs.recipe.KubeRecipe;
import dev.latvian.mods.kubejs.recipe.RecipeKey;
import dev.latvian.mods.kubejs.recipe.component.ComponentRole;
import dev.latvian.mods.kubejs.recipe.component.RecipeComponent;
import dev.latvian.mods.kubejs.recipe.component.RecipeComponentType;
import dev.latvian.mods.kubejs.recipe.component.RecipeComponentValue;
import dev.latvian.mods.kubejs.recipe.component.SimpleRecipeComponent;
import dev.latvian.mods.kubejs.recipe.component.RecipeComponentWithCodec;
import dev.latvian.mods.kubejs.script.ConsoleJS;
import dev.latvian.mods.rhino.type.TypeInfo;
import net.minecraft.resources.ResourceLocation;
import thaumcraft.api.aspects.Aspect;
import thaumcraft.api.aspects.AspectList;
import java.util.Map;
/**
* 将脚本中的源质列表转换为 Thaumcraft {@link AspectList}。
*
* 支持两种脚本写法:
* <pre>{@code
* ['ignis 64', 'potentia 32'] // 字符串数组:"要素名 数量"
* { ignis: 64, potentia: 32 } // 对象:{ 要素名: 数量 }
* }</pre>
*/
public final class AspectListComponent implements RecipeComponent<AspectList> {
public static final AspectListComponent INSTANCE = new AspectListComponent();
private static final RecipeComponentType<?> TYPE = RecipeComponentType.unit(
ResourceLocation.fromNamespaceAndPath("tckubejsbridge", "aspect_list"),
AspectListComponent.INSTANCE);
/** 要素名 → 数量 的对象/数组 编解码(仅作序列化展示,桥接时直接读值) */
private static final Codec<AspectList> CODEC = Codec.unboundedMap(Codec.STRING, Codec.INT)
.flatXmap(
map -> {
try {
return DataResult.success(fromMap(map));
} catch (Exception e) {
return DataResult.error(() -> "Invalid aspect list: " + e.getMessage());
}
},
list -> {
try {
return DataResult.success(toMap(list));
} catch (Exception e) {
return DataResult.error(() -> "Invalid aspect list: " + e.getMessage());
}
});
private AspectListComponent() {
}
@Override
public RecipeComponentType<?> type() {
return TYPE;
}
@Override
public Codec<AspectList> codec() {
return CODEC;
}
@Override
public TypeInfo typeInfo() {
return TypeInfo.of(Object.class);
}
@Override
public AspectList wrap(dev.latvian.mods.kubejs.recipe.RecipeScriptContext cx, Object from) {
if (from instanceof AspectList list) {
return list;
}
if (from instanceof String s) {
return parseString(s);
}
if (from instanceof java.util.List<?> list) {
AspectList result = new AspectList();
for (Object o : list) {
if (o instanceof String s) {
result.add(parseString(s));
} else if (o instanceof AspectList al) {
result.add(al);
}
}
return result;
}
if (from instanceof Map<?, ?> map) {
return fromMap(map);
}
// 兜底:尝试从 JSON 元素解析
if (from instanceof JsonElement json) {
if (json.isJsonObject()) {
return fromJsonObject(json.getAsJsonObject());
}
if (json.isJsonArray()) {
AspectList result = new AspectList();
for (JsonElement e : json.getAsJsonArray()) {
if (e.isJsonPrimitive() && e.getAsJsonPrimitive().isString()) {
result.add(parseString(e.getAsString()));
}
}
return result;
}
}
throw new IllegalArgumentException("Cannot convert " + from + " to AspectList");
}
private static AspectList parseString(String s) {
String[] parts = s.trim().split("\\s+");
Aspect aspect = Aspect.getAspect(parts[0]);
if (aspect == null) {
throw new IllegalArgumentException("Unknown aspect '" + parts[0] + "'");
}
int amount = parts.length > 1 ? Integer.parseInt(parts[1]) : 1;
return new AspectList().add(aspect, amount);
}
private static AspectList fromMap(Map<?, ?> map) {
AspectList result = new AspectList();
for (Map.Entry<?, ?> e : map.entrySet()) {
Aspect aspect = Aspect.getAspect(String.valueOf(e.getKey()));
if (aspect == null) {
throw new IllegalArgumentException("Unknown aspect '" + e.getKey() + "'");
}
result.add(aspect, ((Number) e.getValue()).intValue());
}
return result;
}
private static AspectList fromJsonObject(JsonObject obj) {
AspectList result = new AspectList();
for (Map.Entry<String, JsonElement> e : obj.entrySet()) {
Aspect aspect = Aspect.getAspect(e.getKey());
if (aspect == null) {
throw new IllegalArgumentException("Unknown aspect '" + e.getKey() + "'");
}
result.add(aspect, e.getValue().getAsInt());
}
return result;
}
private static Map<String, Integer> toMap(AspectList list) {
java.util.LinkedHashMap<String, Integer> map = new java.util.LinkedHashMap<>();
for (Aspect aspect : list.getAspects()) {
if (aspect != null) {
map.put(aspect.getTag(), list.getAmount(aspect));
}
}
return map;
}
@Override
public void writeToJson(KubeRecipe recipe, RecipeComponentValue<AspectList> cv, JsonObject json) {
JsonObject obj = new JsonObject();
for (Aspect aspect : cv.value.getAspects()) {
if (aspect != null) {
obj.add(aspect.getTag(), new JsonPrimitive(cv.value.getAmount(aspect)));
}
}
json.add(cv.key.name, obj);
}
@Override
public void readFromJson(KubeRecipe recipe, RecipeComponentValue<AspectList> cv, JsonObject json) {
JsonElement v = json.get(cv.key.name);
if (v != null && v.isJsonObject()) {
cv.value = fromJsonObject(v.getAsJsonObject());
}
}
}
@@ -0,0 +1,23 @@
package tckubejsbridge.kubejs;
import net.minecraft.world.item.ItemStack;
import thaumcraft.api.aspects.AspectList;
/**
* 坩埚炼金配方 KubeRecipe:event.recipes.thaumcraft.crucible(research, result, catalyst, aspects)
*/
public class CrucibleRecipeKJS extends TCRecipeKJS {
public CrucibleRecipeKJS() {
}
@Override
protected void bridgeToTC() {
String research = getValue(TCKubeJSPlugin.RESEARCH);
ItemStack result = getValue(TCKubeJSPlugin.RESULT);
Object catalyst = getValue(TCKubeJSPlugin.CATALYST);
AspectList aspects = getValue(TCKubeJSPlugin.ASPECTS);
TCRecipeBridge.registerCrucible(research, result, catalyst, aspects);
}
}
@@ -0,0 +1,28 @@
package tckubejsbridge.kubejs;
import net.minecraft.world.item.ItemStack;
import thaumcraft.api.aspects.AspectList;
import java.util.List;
/**
* 注魔配方 KubeRecipe:event.recipes.thaumcraft.infusion(research, result, instability, aspects, central, ...components)
*/
public class InfusionRecipeKJS extends TCRecipeKJS {
public InfusionRecipeKJS() {
}
@Override
protected void bridgeToTC() {
String research = getValue(TCKubeJSPlugin.RESEARCH);
ItemStack result = getValue(TCKubeJSPlugin.RESULT);
int instability = getValue(TCKubeJSPlugin.INSTABILITY);
AspectList aspects = getValue(TCKubeJSPlugin.ASPECTS);
ItemStack central = getValue(TCKubeJSPlugin.CENTRAL);
List<ItemStack> components = getValue(TCKubeJSPlugin.COMPONENTS);
TCRecipeBridge.registerInfusion(research, result, instability, aspects, central,
components == null ? new ItemStack[0] : components.toArray(new ItemStack[0]));
}
}
@@ -0,0 +1,44 @@
package tckubejsbridge.kubejs;
import net.minecraft.world.item.ItemStack;
import thaumcraft.api.aspects.AspectList;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 奥术有序合成配方 KubeRecipe:
* event.recipes.thaumcraft.arcane(research, result, aspects, pattern, key)
*
* 其中 pattern 为行字符串数组(如 ['ABA','BCB','ABA']),
* key 为字符→物品映射(如 {A:'minecraft:iron_ingot', B:Item.of('minecraft:gold_ingot')})。
*/
public class ShapedArcaneRecipeKJS extends TCRecipeKJS {
public ShapedArcaneRecipeKJS() {
}
@Override
protected void bridgeToTC() {
String research = getValue(TCKubeJSPlugin.RESEARCH);
ItemStack result = getValue(TCKubeJSPlugin.RESULT);
AspectList aspects = getValue(TCKubeJSPlugin.ASPECTS);
List<String> pattern = getValue(TCKubeJSPlugin.PATTERN);
dev.latvian.mods.kubejs.util.TinyMap<Character, ItemStack> key = getValue(TCKubeJSPlugin.KEY);
// 组装 TC addArcaneCraftingRecipe 参数:pattern 行 + 字符/物品映射
List<Object> args = new ArrayList<>();
if (pattern != null) {
args.addAll(pattern);
}
if (key != null) {
for (dev.latvian.mods.kubejs.util.TinyMap.Entry<Character, ItemStack> e : key.entries()) {
args.add(e.key());
args.add(e.value());
}
}
TCRecipeBridge.registerArcane(research, result, aspects, args.toArray());
}
}
@@ -0,0 +1,27 @@
package tckubejsbridge.kubejs;
import net.minecraft.world.item.ItemStack;
import thaumcraft.api.aspects.AspectList;
import java.util.List;
/**
* 奥术无序合成配方 KubeRecipe:
* event.recipes.thaumcraft.arcane_shapeless(research, result, aspects, ...ingredients)
*/
public class ShapelessArcaneRecipeKJS extends TCRecipeKJS {
public ShapelessArcaneRecipeKJS() {
}
@Override
protected void bridgeToTC() {
String research = getValue(TCKubeJSPlugin.RESEARCH);
ItemStack result = getValue(TCKubeJSPlugin.RESULT);
AspectList aspects = getValue(TCKubeJSPlugin.ASPECTS);
List<ItemStack> ingredients = getValue(TCKubeJSPlugin.INGREDIENTS);
TCRecipeBridge.registerArcaneShapeless(research, result, aspects,
ingredients == null ? new ItemStack[0] : ingredients.toArray(new ItemStack[0]));
}
}
@@ -0,0 +1,154 @@
package tckubejsbridge.kubejs;
import dev.latvian.mods.kubejs.plugin.KubeJSPlugin;
import dev.latvian.mods.kubejs.recipe.RecipeKey;
import dev.latvian.mods.kubejs.recipe.component.ComponentRole;
import dev.latvian.mods.kubejs.recipe.component.ItemStackComponent;
import dev.latvian.mods.kubejs.recipe.component.NumberComponent;
import dev.latvian.mods.kubejs.recipe.component.RecipeComponent;
import dev.latvian.mods.kubejs.recipe.component.RecipeComponentType;
import dev.latvian.mods.kubejs.recipe.component.RecipeComponentTypeRegistry;
import dev.latvian.mods.kubejs.recipe.component.StringComponent;
import dev.latvian.mods.kubejs.recipe.schema.KubeRecipeFactory;
import dev.latvian.mods.kubejs.recipe.schema.RecipeSchema;
import dev.latvian.mods.kubejs.recipe.schema.RecipeSchemaRegistry;
import dev.latvian.mods.kubejs.script.BindingRegistry;
import dev.latvian.mods.kubejs.script.ConsoleJS;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import thaumcraft.api.aspects.AspectList;
import java.util.List;
import java.util.Map;
/**
* KubeJS 插件:将 Thaumcraft 配方系统桥接到 KubeJS 脚本。
*
* 提供:
* <ul>
* <li>event.recipes.thaumcraft.infusion(...) — 注魔配方</li>
* <li>event.recipes.thaumcraft.crucible(...) — 坩埚炼金配方</li>
* <li>event.recipes.thaumcraft.arcane(...) — 奥术有序合成配方</li>
* <li>event.recipes.thaumcraft.arcane_shapeless(...) — 奥术无序合成配方</li>
* <li>event.thaumcraft.addObjectTag(...) — 物品固有要素</li>
* </ul>
*
* 发现方式:{@code kubejs.plugins.txt} 列出本类全名。
*/
public class TCKubeJSPlugin implements KubeJSPlugin {
// ---- 共享 RecipeKey ----
static final RecipeKey<String> RESEARCH = StringComponent.STRING.instance().otherKey("research").alt("key");
static final RecipeKey<ItemStack> RESULT = ItemStackComponent.ITEM_STACK.instance().outputKey("result");
static final RecipeKey<AspectList> ASPECTS = AspectListComponent.INSTANCE.otherKey("aspects").alt("aspect");
static final RecipeKey<Integer> INSTABILITY = NumberComponent.INT.otherKey("instability");
// 注魔
static final RecipeKey<ItemStack> CENTRAL = ItemStackComponent.ITEM_STACK.instance().inputKey("central");
static final RecipeKey<List<ItemStack>> COMPONENTS = ItemStackComponent.ITEM_STACK.instance().asListOrSelf().inputKey("components");
// 坩埚:催化剂接受任意对象(脚本传物品 id 字符串或 ItemStack,由自定义组件解析)
static final RecipeKey<Object> CATALYST = ObjectCatalystComponent.INSTANCE.otherKey("catalyst");
// 奥术有序
static final RecipeKey<List<String>> PATTERN = StringComponent.STRING.instance().asList().otherKey("pattern");
static final RecipeKey<dev.latvian.mods.kubejs.util.TinyMap<Character, ItemStack>> KEY =
ItemStackComponent.ITEM_STACK.instance().asPatternKey().otherKey("key");
// 奥术无序
static final RecipeKey<List<ItemStack>> INGREDIENTS = ItemStackComponent.ITEM_STACK.instance().asListOrSelf().inputKey("ingredients");
/** 坩埚催化剂组件:接受任意脚本对象,桥接时转换为 TC 期望的 Object(ItemStack/TagKey/Ingredient) */
static final class ObjectCatalystComponent implements RecipeComponent<Object> {
static final ObjectCatalystComponent INSTANCE = new ObjectCatalystComponent();
private static final RecipeComponentType<?> TYPE = RecipeComponentType.unit(
ResourceLocation.fromNamespaceAndPath("tckubejsbridge", "object_catalyst"), INSTANCE);
@Override public RecipeComponentType<?> type() { return TYPE; }
@Override public com.mojang.serialization.Codec<Object> codec() {
return com.mojang.serialization.Codec.STRING.xmap(
s -> s, o -> String.valueOf(o));
}
@Override public dev.latvian.mods.rhino.type.TypeInfo typeInfo() {
return dev.latvian.mods.rhino.type.TypeInfo.of(Object.class);
}
@Override public Object wrap(dev.latvian.mods.kubejs.recipe.RecipeScriptContext cx, Object from) {
return from;
}
}
@Override
public void registerRecipeComponents(RecipeComponentTypeRegistry registry) {
registry.register(AspectListComponent.INSTANCE.type());
registry.register(ObjectCatalystComponent.TYPE);
}
@Override
public void registerRecipeSchemas(RecipeSchemaRegistry registry) {
var tc = registry.namespace("thaumcraft");
// typeOverride 指向真实存在的 serializer:TC 配方不走原版 RecipeManager,
// 但 KubeJS 创建配方对象时要求 serializer 存在于 BuiltInRegistries.RECIPE_SERIALIZER,
// 否则抛 "Serializer for type X is not found"。桥接后输出占位 JSON,故用 minecraft:crafting_shapeless。
var vanillaType = ResourceLocation.withDefaultNamespace("crafting_shapeless");
tc.register("infusion", new RecipeSchema(
RESEARCH, RESULT, INSTABILITY, ASPECTS, CENTRAL, COMPONENTS)
.typeOverride(vanillaType)
.factory(new KubeRecipeFactory(
ResourceLocation.fromNamespaceAndPath("tckubejsbridge", "infusion"),
InfusionRecipeKJS.class, InfusionRecipeKJS::new)));
tc.register("crucible", new RecipeSchema(
RESEARCH, RESULT, CATALYST, ASPECTS)
.typeOverride(vanillaType)
.factory(new KubeRecipeFactory(
ResourceLocation.fromNamespaceAndPath("tckubejsbridge", "crucible"),
CrucibleRecipeKJS.class, CrucibleRecipeKJS::new)));
tc.register("arcane", new RecipeSchema(
RESEARCH, RESULT, ASPECTS, PATTERN, KEY)
.typeOverride(vanillaType)
.factory(new KubeRecipeFactory(
ResourceLocation.fromNamespaceAndPath("tckubejsbridge", "arcane"),
ShapedArcaneRecipeKJS.class, ShapedArcaneRecipeKJS::new)));
tc.register("arcane_shapeless", new RecipeSchema(
RESEARCH, RESULT, ASPECTS, INGREDIENTS)
.typeOverride(vanillaType)
.factory(new KubeRecipeFactory(
ResourceLocation.fromNamespaceAndPath("tckubejsbridge", "arcane_shapeless"),
ShapelessArcaneRecipeKJS.class, ShapelessArcaneRecipeKJS::new)));
}
@Override
public void registerBindings(BindingRegistry bindings) {
bindings.add("thaumcraft", new ThaumcraftBindings());
}
/** 脚本侧绑定:event.thaumcraft.addObjectTag(...) / removeInfusion(...) 等 */
public static final class ThaumcraftBindings {
public void addObjectTag(ItemStack item, AspectList aspects) {
if (item == null || item.isEmpty()) {
ConsoleJS.SERVER.error("TC-KubeJS-Bridge: addObjectTag got empty item");
return;
}
thaumcraft.api.ThaumcraftApi.registerObjectTag(item, aspects);
ConsoleJS.SERVER.info("TC-KubeJS-Bridge: registered object tag for " + item);
}
/** 按 research 键移除 TC 已有注魔配方(先删后注册 = 真正替换) */
public boolean removeInfusion(String research) {
return TCRecipeBridge.removeInfusion(research);
}
/** 按 research 键移除 TC 已有奥术(有序/无序)配方 */
public boolean removeArcane(String research) {
return TCRecipeBridge.removeArcane(research);
}
/** 清空去重集合(/reload 时若需重新注册可调用) */
public void clearRegistered() {
TCRecipeBridge.clearRegistered();
}
}
}
@@ -0,0 +1,118 @@
package tckubejsbridge.kubejs;
import com.google.gson.JsonObject;
import dev.latvian.mods.kubejs.script.ConsoleJS;
import net.minecraft.world.item.ItemStack;
import thaumcraft.api.ThaumcraftApi;
import thaumcraft.api.aspects.AspectList;
import java.util.HashSet;
import java.util.Set;
/**
* 桥接工具:将 KubeJS 脚本中解析出的配方参数实际注册到 Thaumcraft。
*
* 注意:TC 注魔/炼金/奥术配方全部走 {@link ThaumcraftApi#getCraftingRecipes()} 内存列表,
* 不会进入原版 RecipeManager;因此本类在脚本执行时直接调用 TC API 完成注册,
* 并生成一个无害的占位 JSON(避免 KubeJS 把空配方交给 RecipeManager 时报错)。
*/
public final class TCRecipeBridge {
private static final Set<String> REGISTERED_RESEARCH = new HashSet<>();
private TCRecipeBridge() {
}
/** 按研究键去重,防止 /reload 重复注册 */
private static boolean alreadyRegistered(String research) {
return !REGISTERED_RESEARCH.add(research);
}
/** 清空去重集合(供 KubeJS 事件在 /reload 时调用,允许重新注册) */
public static void clearRegistered() {
REGISTERED_RESEARCH.clear();
}
/**
* 按 research 键从 TC 配方列表中移除已有的注魔配方。
* 用于脚本"先删原配方,再注册新配方",实现真正替换。
*
* @return 是否至少移除了一条
*/
public static boolean removeInfusion(String research) {
boolean removed = ThaumcraftApi.getCraftingRecipes().removeIf(r ->
r instanceof thaumcraft.api.crafting.InfusionRecipe ir && research.equals(ir.research()));
if (removed) {
ConsoleJS.SERVER.info("TC-KubeJS-Bridge: removed infusion recipe(s) for research '" + research + "'");
}
// 允许该键后续重新注册
REGISTERED_RESEARCH.remove(research);
return removed;
}
/** 按 research 键从 TC 配方列表中移除已有的奥术(有序/无序)配方 */
public static boolean removeArcane(String research) {
boolean removed = ThaumcraftApi.getCraftingRecipes().removeIf(r -> {
if (r instanceof thaumcraft.api.crafting.ShapedArcaneRecipe sr) return research.equals(sr.research);
if (r instanceof thaumcraft.api.crafting.ShapelessArcaneRecipe slr) return research.equals(slr.research);
return false;
});
if (removed) {
ConsoleJS.SERVER.info("TC-KubeJS-Bridge: removed arcane recipe(s) for research '" + research + "'");
}
// 允许该键后续重新注册
REGISTERED_RESEARCH.remove(research);
return removed;
}
public static void registerInfusion(String research, ItemStack result, int instability,
AspectList aspects, ItemStack central, ItemStack... components) {
if (alreadyRegistered(research)) {
ConsoleJS.SERVER.info("TC-KubeJS-Bridge: infusion recipe '" + research + "' already registered, skipping");
return;
}
ThaumcraftApi.addInfusionCraftingRecipe(research, result, instability, aspects, central, components);
ConsoleJS.SERVER.info("TC-KubeJS-Bridge: registered infusion recipe '" + research + "'");
}
public static void registerCrucible(String research, ItemStack result, Object catalyst, AspectList aspects) {
if (alreadyRegistered(research)) {
ConsoleJS.SERVER.info("TC-KubeJS-Bridge: crucible recipe '" + research + "' already registered, skipping");
return;
}
ThaumcraftApi.addCrucibleRecipe(research, result, catalyst, aspects);
ConsoleJS.SERVER.info("TC-KubeJS-Bridge: registered crucible recipe '" + research + "'");
}
public static void registerArcane(String research, ItemStack result, AspectList aspects, Object... recipe) {
if (alreadyRegistered(research)) {
ConsoleJS.SERVER.info("TC-KubeJS-Bridge: arcane recipe '" + research + "' already registered, skipping");
return;
}
ThaumcraftApi.addArcaneCraftingRecipe(research, result, aspects, recipe);
ConsoleJS.SERVER.info("TC-KubeJS-Bridge: registered arcane recipe '" + research + "'");
}
public static void registerArcaneShapeless(String research, ItemStack result, AspectList aspects, Object... recipe) {
if (alreadyRegistered(research)) {
ConsoleJS.SERVER.info("TC-KubeJS-Bridge: arcane shapeless recipe '" + research + "' already registered, skipping");
return;
}
ThaumcraftApi.addShapelessArcaneCraftingRecipe(research, result, aspects, recipe);
ConsoleJS.SERVER.info("TC-KubeJS-Bridge: registered arcane shapeless recipe '" + research + "'");
}
/**
* 生成无害占位配方 JSON,替换 KubeJS 对自定义配方自动生成的 JSON,
* 使 RecipeManager 能正常解析但不会产生任何实际效果。
*/
public static JsonObject placeholderRecipe() {
JsonObject json = new JsonObject();
json.addProperty("type", "minecraft:crafting_shapeless");
json.add("ingredients", new com.google.gson.JsonArray());
JsonObject result = new JsonObject();
result.addProperty("item", "minecraft:air");
json.add("result", result);
return json;
}
}
@@ -0,0 +1,30 @@
package tckubejsbridge.kubejs;
import com.google.gson.JsonObject;
import dev.latvian.mods.kubejs.recipe.KubeRecipe;
/**
* TC 配方 KubeRecipe 基类。
*
* 覆写 {@link #serializeChanges()}:脚本执行完成后(值已全部设置)调用子类实现的
* {@link #bridgeToTC()} 完成 TC 内存配方注册,并用无害占位 JSON 替换序列化输出,
* 避免 KubeJS 将自定义配方误交给原版 RecipeManager 后产生异常配方。
*/
public abstract class TCRecipeKJS extends KubeRecipe {
public TCRecipeKJS() {
}
/** 由子类实现:读取 RecipeKey 值并调用 TC API 注册配方 */
protected abstract void bridgeToTC();
@Override
public KubeRecipe serializeChanges() {
if (newRecipe || hasChanged()) {
bridgeToTC();
// 占位 JSON,避免污染 RecipeManager
this.json = TCRecipeBridge.placeholderRecipe();
}
return this;
}
}