# TC KubeJS Bridge (tckubejsbridge) > **English** | [中文](README.md) Bridges the **Thaumcraft 4 (port)** recipe and research systems to **KubeJS**. Modpack authors can use scripts in `kubejs/server_scripts/` to register: - Infusion recipes - Crucible recipes - Shaped / shapeless arcane recipes - Item aspect tags - Custom research categories / research nodes - Replacing existing TC recipes Author: AlkSur | License: GPL-3.0 --- ## Requirements | Mod | Version | Relation | |---|---|---| | NeoForge | 21.1.234 | Required | | Minecraft | 1.21.1 | Required | | Thaumcraft (TC4 port) | 0.2.2.34-port.1 | Required | | KubeJS (NeoForge) | 2101.7.x | Required (plugin depends on its API) | > KubeJS is declared **optional** in `neoforge.mods.toml`: without KubeJS the plugin part > simply does not load, and the game still starts. --- ## Building & Installation The project lives at `modules/tc-kubejs-bridge` inside a multi-module Gradle build. ```bash # Run from the repository root gradle :tckubejsbridge:build ``` Artifact: ```text modules/tc-kubejs-bridge/build/libs/KubeJS Thaumcraft-+.jar ``` For example: ```text KubeJS Thaumcraft-1.0.0+21.1.234.jar ``` Put the jar into your `mods/` folder. --- ## Quick Start 1. Create a `.js` file under `kubejs/server_scripts/`. 2. Use `ServerEvents.recipes(event => { ... })`. 3. Run `/reload` or restart the game. All `thaumcraft.xxx` calls are **top-level bindings**; do not use `event.thaumcraft.xxx`. --- ## Recipe Registration ### 1. Infusion Recipe ```js ServerEvents.recipes(event => { event.recipes.thaumcraft.infusion( 'MY_INFUSION', // research key (globally unique) 'minecraft:diamond', // result 3, // instability ['ignis 64', 'potentia 32'], // required aspects 'minecraft:golden_apple', // central item 'minecraft:blaze_rod', // component 'minecraft:ender_pearl' ); }); ``` ### 2. Crucible Recipe ```js ServerEvents.recipes(event => { event.recipes.thaumcraft.crucible( 'MY_CRUCIBLE', 'minecraft:diamond', 'minecraft:coal', // catalyst { aer: 16, ignis: 8 } // object syntax also works ); }); ``` ### 3. Shaped Arcane Recipe ```js ServerEvents.recipes(event => { event.recipes.thaumcraft.arcane( 'MY_ARCANE', 'minecraft:diamond_sword', ['ignis 16', 'ordo 8'], [' I ', ' I ', ' S '], // pattern: up to 3 rows { I: 'minecraft:iron_ingot', S: 'minecraft:stick' } ); }); ``` ### 4. Shapeless Arcane Recipe > Note: `arcane_shapeless` ingredients must be provided as **one array**. ```js ServerEvents.recipes(event => { event.recipes.thaumcraft.arcane_shapeless( 'MY_SHAPELESS', 'minecraft:diamond_helmet', ['aer 8', 'aqua 8'], [ 'minecraft:leather_helmet', 'minecraft:diamond', 'minecraft:diamond', 'minecraft:diamond' ] ); }); ``` --- ## Item Aspect Tags ```js ServerEvents.recipes(event => { // add thaumcraft.addObjectTag('minecraft:stick', { aer: 1, ignis: 2 }); // remove thaumcraft.removeObjectTag('minecraft:stick'); }); ``` --- ## Replacing Existing Recipes ```js ServerEvents.recipes(event => { // remove first thaumcraft.removeInfusion('INFUSIONPROVIDER'); // infusion // thaumcraft.removeArcane('RESEARCH_KEY'); // arcane // thaumcraft.removeCrucible('RESEARCH_KEY'); // crucible // then register a new recipe event.recipes.thaumcraft.infusion( 'INFUSIONPROVIDER', 'thaumicenergistics:infusion_provider', 0, ['auram 10'], 'ae2:interface', 'minecraft:oak_planks' ); }); ``` --- ## Custom Research Categories / Nodes ### Add a Research Category ```js ServerEvents.recipes(event => { thaumcraft.addResearchCategory( 'MY_CATEGORY', 'textures/research/cat_icon.png', 'textures/research/cat_bg.png' ); }); ``` - A two-argument form is also available: `addResearchCategory('MY_CATEGORY', 'textures/research/cat_icon.png')` - Paths without a namespace default to the `kubejs:` namespace. ### Add a Research Node ```js ServerEvents.recipes(event => { thaumcraft.addResearchNode({ key: 'MY_RESEARCH', category: 'MY_CATEGORY', icon: 'textures/research/my_research.png', parents: ['PARENT_KEY'], autoUnlock: true, // unlocked by default virtual: false, // visible in the Thaumonomicon; true hides it hidden: false, col: 0, row: 0, complexity: 1, aspects: { aer: 1, ignis: 1 } }); }); ``` Supported fields: | Field | Description | |---|---| | `key` | Unique research node ID | | `category` | Existing research category | | `icon` | Icon path | | `parents` / `parentsHidden` / `siblings` | Research dependencies | | `autoUnlock` | Unlocked by default | | `virtual` | Hidden from the Thaumonomicon when `true` | | `hidden` / `concealed` / `lost` | Visibility flags | | `special` / `stub` / `secondary` / `round` | Research type flags | | `col` / `row` / `complexity` | Position and complexity on the research map | | `aspects` | Associated aspects | ### Icon File Location If you write: ```js icon: 'textures/research/my_research.png' ``` The file should be at: ```text kubejs/assets/kubejs/textures/research/my_research.png ``` You can also use a full path: ```js icon: 'kubejs:textures/research/my_research.png' ``` ### Research Names / Descriptions TC research names use language files. Add this to `kubejs/assets/kubejs/lang/en_us.json`: ```json { "tc.research_category.MY_CATEGORY": "My Category", "tc.research_name.MY_RESEARCH": "My Research", "tc.research_text.MY_RESEARCH": "This is a research description." } ``` For Chinese, add the same keys to `kubejs/assets/kubejs/lang/zh_cn.json`. --- ## Aspect Tag Reference (actual values in the TC4 port) **Important: the port uses the original Latin tags, not the TC4 English/localized names.** | Aspect | Tag | Aspect | Tag | |---|---|---|---| | Air | `aer` | Earth | `terra` | | Fire | `ignis` | Water | `aqua` | | Order | `ordo` | Entropy | `perditio` | | Void | `vacuos` | Light | `lux` | | Weather | `tempestas` | Motion | `motus` | | Crystal | `vitreus` | Life | `victus` | | Energy | `potentia` | Exchange | `permutatio` | | Death | `mortuus` | Darkness | `tenebrae` | | Soul | `spiritus` | Eldritch | `alienis` | | Magic | `praecantatio` | Aura | `auram` | | Slime | `limus` | Plant | `herba` | | Tree | `arbor` | Beast | `bestia` | | Flesh | `corpus` | Undead | `exanimis` | | Mind | `cognitio` | Senses | `sensus` | | Man | `humanus` | Tool | `instrumentum` | | Greed | `lucrum` | Craft | `fabrico` | | Cloth | `pannus` | Mechanism | `machina` | ## Hot Reload | Content | `/reload` | Notes | |---|---|---| | Infusion / Crucible / Arcane recipes | ✅ | Can be hot-reloaded | | Item aspect tags | ✅ | Can be hot-reloaded | | Research categories / nodes | ❌ | Requires a game restart | ### Research key notes - If `research` is **not** an existing TC research, the bridge registers a default-unlocked virtual research node and treats the recipe as having **no research requirement**. - If `research` **is** an existing TC research, the recipe still requires that research to be completed. - Research keys are globally unique; duplicate keys are skipped. ## Troubleshooting | Symptom | Cause | |---|---| | `Constructor for thaumcraft:arcane_shapeless with N arguments not found` | Ingredients were not placed in an array | | `Unknown aspect 'xxx'` | Wrong aspect tag | | `Item with ID xxx does not exist` | Wrong item id | | Research category name shows `tc.research_category.XXX` | Missing language file | | Research icon / background is purple-black | Texture missing, wrong path, not a PNG, or KubeJS assets not loaded | | `event.thaumcraft is undefined` | Use top-level `thaumcraft.xxx`, not `event.thaumcraft.xxx` | ## Project Layout ```text src/main/java/tckubejsbridge/ ├── TCKubeJSBridge.java // @Mod main class └── kubejs/ ├── TCKubeJSPlugin.java // KubeJS plugin: schemas + bindings ├── TCRecipeKJS.java // Custom KubeRecipe base ├── TCRecipeBridge.java // TC API calls / dedup / removal / research registration ├── AspectListComponent.java // Aspect list component ├── InfusionRecipeKJS.java // Infusion recipe ├── CrucibleRecipeKJS.java // Crucible recipe ├── ShapedArcaneRecipeKJS.java // Shaped arcane recipe └── ShapelessArcaneRecipeKJS.java // Shapeless arcane recipe src/main/resources/ ├── kubejs.plugins.txt // Plugin discovery file └── META-INF/neoforge.mods.toml example/infusion_example.js // Example script ```