├── .github └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md └── docs ├── base.yml ├── cbf.yml ├── cbf ├── assets │ ├── js │ │ └── mathjax.js │ ├── logo.png │ └── stylesheets │ │ └── extra.css ├── binaryadapter.md ├── compound.md ├── index.md ├── instancecreator.md └── serialization.md ├── invui.yml ├── invui ├── assets │ ├── js │ │ └── mathjax.js │ ├── logo.png │ └── stylesheets │ │ └── extra.css ├── basics.md ├── guis │ ├── index.md │ ├── normal.md │ ├── paged.md │ ├── scroll.md │ └── tab.md ├── index.md ├── inventory.md ├── items.md ├── localization.md ├── structure.md └── windows.md ├── nova.yml └── nova ├── addon ├── abilities.md ├── addon-configuration.md ├── attachments.md ├── blocks │ ├── block-behaviors.md │ └── creating-blocks.md ├── configs.md ├── contexts.md ├── entity-variants.md ├── fonts │ ├── actionbar.md │ ├── bossbar.md │ ├── fonts.md │ └── guitextures.md ├── hitboxes.md ├── index.md ├── initialization.md ├── items │ ├── creating-items.md │ ├── enchantments.md │ ├── equipment.md │ ├── item-behaviors.md │ ├── recipes.md │ └── tools.md ├── migration-guide │ ├── 0.11-0.12.md │ ├── 0.12-0.13.md │ ├── 0.13-0.14.md │ ├── 0.14-0.15.md │ ├── 0.15-0.16.md │ ├── 0.16-0.17.md │ ├── 0.17-0.18.md │ └── 0.18-0.19.md ├── misc │ ├── events.md │ ├── project-distributors.md │ └── scheduler.md ├── resourcepack │ ├── addon-assets.md │ └── build-logic.md ├── tile-entity-networks │ ├── custom-network-types.md │ ├── introduction.md │ └── networked-tile-entity.md ├── tile-entity │ ├── gui.md │ ├── introduction.md │ └── region.md └── worldgen │ ├── biome.md │ ├── carver.md │ ├── codec.md │ ├── features │ ├── configurations │ │ ├── bamboo.md │ │ ├── basalt-columns.md │ │ ├── block-column.md │ │ ├── block-pile.md │ │ ├── delta.md │ │ ├── disk.md │ │ ├── dripstone-cluster.md │ │ ├── end-gateway.md │ │ ├── end-spike.md │ │ ├── fill-layer.md │ │ ├── forest-rock.md │ │ ├── fossil.md │ │ ├── geode.md │ │ ├── huge-fungus.md │ │ ├── huge-mushrooms.md │ │ ├── iceberg.md │ │ ├── lake.md │ │ ├── large-dripstone.md │ │ ├── multiface-growth.md │ │ ├── nether-forest-vegetation.md │ │ ├── ores.md │ │ ├── pointed-dripstone.md │ │ ├── random-patch.md │ │ ├── random-selector.md │ │ ├── replace-blobs.md │ │ ├── replace-single-block.md │ │ ├── root-system.md │ │ ├── sculk-patch.md │ │ ├── sea-pickle.md │ │ ├── seagrass.md │ │ ├── simple-block.md │ │ ├── simple-random-selector.md │ │ ├── spring-feature.md │ │ ├── tree.md │ │ ├── twisting-vines.md │ │ ├── underwater-magma.md │ │ └── vegetation-patch.md │ ├── custom-feature.md │ ├── features.md │ └── placed-feature.md │ ├── inject │ └── biome.md │ ├── types │ ├── block-predicate.md │ ├── block-state-provider.md │ ├── block-state.md │ ├── height-provider.md │ └── number-provider.md │ └── worldgen.md ├── admin ├── compatibility │ ├── index.md │ ├── itemsadder.md │ └── nexo.md ├── configuration.md ├── faq.md ├── recipes │ ├── index.md │ ├── machines.md │ └── vanilla.md └── setup.md ├── api ├── blocks │ ├── blockmanager.md │ └── blockregistry.md ├── events │ ├── novaloaddataevent.md │ └── tileentitybreakblockevent.md ├── index.md ├── items │ └── index.md ├── player │ └── wailamanager.md ├── protection │ └── protectionintegration.md └── tileentity │ ├── tileentity.md │ └── tileentitymanager.md ├── assets ├── js │ └── mathjax.js ├── logo.png └── stylesheets │ └── extra.css └── index.md /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | permissions: 8 | contents: write 9 | jobs: 10 | deploy: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | 15 | - name: Configure Git Credentials 16 | run: | 17 | git config user.name github-actions[bot] 18 | git config user.email 41898282+github-actions[bot]@users.noreply.github.com 19 | 20 | - uses: actions/setup-python@v5 21 | with: 22 | python-version: 3.x 23 | 24 | - run: pip install mkdocs-material 25 | 26 | - run: mkdocs build -f docs/nova.yml -d ../build/nova 27 | - run: mkdocs build -f docs/invui.yml -d ../build/invui 28 | - run: mkdocs build -f docs/cbf.yml -d ../build/cbf 29 | 30 | - name: Deploy Docs 31 | uses: JamesIves/github-pages-deploy-action@v4 32 | with: 33 | folder: build -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | docs/.obsidian 3 | /.scripts 4 | 5 | # Mac Stuff 6 | .DS_Store 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Docs 2 | Repository for xenondevs docs: [nova](https://xenondevs.xyz/docs/nova/) [invui](https://xenondevs.xyz/docs/invui/) [cbf](https://xenondevs.xyz/docs/cbf/) 3 | -------------------------------------------------------------------------------- /docs/base.yml: -------------------------------------------------------------------------------- 1 | repo_url: https://github.com/xenondevs/Docs 2 | repo_name: xenondevs/Docs 3 | copyright: Copyright © 2025 xenondevs 4 | 5 | extra: 6 | social: 7 | - icon: fontawesome/brands/github 8 | link: https://github.com/xenondevs 9 | - icon: fontawesome/brands/discord 10 | link: https://discord.com/invite/EpVMXtXB2t 11 | 12 | theme: 13 | name: material 14 | logo: assets/logo.png 15 | palette: 16 | scheme: slate 17 | icon: 18 | repo: fontawesome/brands/github 19 | 20 | markdown_extensions: 21 | - pymdownx.highlight: 22 | anchor_linenums: true 23 | - pymdownx.tabbed: 24 | alternate_style: true 25 | - pymdownx.tasklist: 26 | custom_checkbox: true 27 | - pymdownx.emoji: 28 | emoji_index: !!python/name:material.extensions.emoji.twemoji 29 | emoji_generator: !!python/name:material.extensions.emoji.to_svg 30 | - pymdownx.arithmatex: 31 | generic: true 32 | - pymdownx.superfences: 33 | custom_fences: 34 | - name: mermaid 35 | class: mermaid 36 | format: !!python/name:pymdownx.superfences.fence_code_format 37 | - pymdownx.inlinehilite 38 | - pymdownx.snippets 39 | - pymdownx.superfences 40 | - pymdownx.details 41 | - admonition 42 | - attr_list 43 | - md_in_html 44 | - tables 45 | - def_list 46 | - footnotes 47 | 48 | plugins: 49 | - search 50 | 51 | extra_css: 52 | - assets/stylesheets/extra.css 53 | 54 | extra_javascript: 55 | - assets/js/mathjax.js 56 | - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js 57 | - https://unpkg.com/mermaid@10/dist/mermaid.min.js 58 | -------------------------------------------------------------------------------- /docs/cbf.yml: -------------------------------------------------------------------------------- 1 | INHERIT: base.yml 2 | docs_dir: cbf 3 | site_name: CBF Documentation 4 | site_url: https://xenondevs.xyz/docs/cbf/ 5 | edit_uri: edit/main/docs/cbf/ 6 | 7 | theme: 8 | features: 9 | - navigation.footer 10 | - content.tabs.link 11 | - content.code.annotate 12 | - content.code.copy 13 | - content.code.select 14 | 15 | nav: 16 | - "Getting Started": "index.md" 17 | - "Binary Adapters": "binaryadapter.md" 18 | - "Instance Creators": "instancecreator.md" 19 | - "Serialization": "serialization.md" 20 | - "Compounds": "compound.md" 21 | -------------------------------------------------------------------------------- /docs/cbf/assets/js/mathjax.js: -------------------------------------------------------------------------------- 1 | window.MathJax = { 2 | tex: { 3 | inlineMath: [["\\(", "\\)"]], 4 | displayMath: [["\\[", "\\]"]], 5 | processEscapes: true, 6 | processEnvironments: true 7 | }, 8 | options: { 9 | ignoreHtmlClass: ".*|", 10 | processHtmlClass: "arithmatex", 11 | enableMenu: false 12 | } 13 | }; 14 | 15 | document$.subscribe(() => { 16 | MathJax.typesetPromise() 17 | }) -------------------------------------------------------------------------------- /docs/cbf/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xenondevs/Docs/59ad882f0f1c9eb9dc27ce136a68b70dfde63db7/docs/cbf/assets/logo.png -------------------------------------------------------------------------------- /docs/cbf/assets/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | /*noinspection CssUnresolvedCustomProperty*/ 2 | :root > * { 3 | --md-primary-fg-color: #1B1D1F; 4 | --md-primary-bg-color: #FFFFFF; 5 | --md-default-bg-color: #151719; 6 | --md-typeset-a-color: var(--md-code-hl-function-color); 7 | --md-default-fg-color--light: #abb9cb; 8 | 9 | --md-code-hl-color: hsla(163, 34%, 67%, 0.5); 10 | --md-code-hl-number-color: #0aa370; 11 | --md-code-hl-special-color: #73a0d3; 12 | --md-code-hl-function-color: #7eb6f6; 13 | --md-code-hl-constant-color: #abb9cb; 14 | --md-code-hl-keyword-color: #47ebb4; 15 | --md-code-hl-string-color: #7eb6f6; 16 | --md-code-hl-name-color: var(--md-code-fg-color); 17 | --md-code-hl-operator-color: #47ebb4; 18 | --md-code-hl-punctuation-color: #abb9cb; 19 | --md-code-hl-comment-color: #abb9cb; 20 | --md-code-hl-generic-color: var(--md-default-fg-color--light); 21 | --md-code-hl-variable-color: #7eb6f6; 22 | } 23 | 24 | @media only screen and (min-width: 76.25em) { 25 | .md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link:not(.md-nav__container) { 26 | width: 90%; 27 | } 28 | 29 | .md-main__inner { 30 | max-width: none; 31 | } 32 | 33 | .md-content { 34 | max-width: 56.5%; 35 | } 36 | 37 | .md-sidebar--primary { 38 | margin-left: 3.75%; 39 | margin-right: 3.75%; 40 | } 41 | 42 | .md-sidebar--secondary { 43 | margin-left: 3.75%; 44 | margin-right: 3.75%; 45 | -webkit-transform: none; 46 | transform: none; 47 | } 48 | 49 | body::-webkit-scrollbar { 50 | width: 15px; 51 | height: 15px; 52 | } 53 | 54 | body::-webkit-scrollbar-thumb { 55 | background-color: #21222c; 56 | border-radius: 15px; 57 | } 58 | 59 | body::-webkit-scrollbar-track { 60 | background-color: #2e303e; 61 | } 62 | 63 | body { 64 | scrollbar-color: #21222c #2e303e; 65 | } 66 | 67 | } 68 | 69 | .md-nav { 70 | font-size: 14px; 71 | line-height: 1.4; 72 | } 73 | 74 | .md-nav__link { 75 | display: inline-flex; 76 | } 77 | 78 | .md-typeset { 79 | font-size: .7rem; 80 | line-height: 1.5; 81 | } 82 | 83 | .md-typeset :not(pre) > code { 84 | background-color: #23262b; 85 | } 86 | 87 | .text-center { 88 | text-align: center !important; 89 | } 90 | 91 | .md-tabs { 92 | box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 0.2rem, rgba(0, 0, 0, 0.2) 0px 0.2rem 0.4rem; 93 | transition: transform 0.25s cubic-bezier(0.1, 0.7, 0.1, 1) 0s, box-shadow 0.25s ease 0s; 94 | } 95 | 96 | .md-nav__item .md-nav__link--active { 97 | color: #768390; 98 | } 99 | 100 | .md-typeset .admonition { 101 | background-color: var(--md-primary-fg-color); 102 | border-width: 0; 103 | border-left-width: 4px; 104 | } 105 | 106 | .md-typeset details { 107 | background-color: var(--md-primary-fg-color); 108 | border-width: 0; 109 | border-left-width: 4px; 110 | } 111 | 112 | .md-typeset table:not([class]) { 113 | border-width: 0; 114 | box-shadow: 0 0.2rem 0.5rem hsla(0, 0%, 0%, 0.3),0 0 0.05rem hsla(0, 0%, 0%, 0.2); 115 | border-radius: 5px; 116 | } 117 | 118 | .md-typeset th { 119 | background-color: #111111; 120 | } 121 | 122 | .md-typeset td { 123 | background-color: #1b1d1f; 124 | } 125 | 126 | .md-typeset tbody tr:first-child td { 127 | border-color: #3593ff; 128 | } 129 | 130 | .md-footer { 131 | background-color: var(--md-primary-fg-color); 132 | } 133 | 134 | .md-footer-meta { 135 | background: none; 136 | } 137 | 138 | .md-typeset .md-button { 139 | color: #fff; 140 | } 141 | 142 | .md-typeset .md-button:hover { 143 | background-color: rgba(255, 255, 255, 0.1); 144 | border-color: rgba(255, 255, 255, 0); 145 | } -------------------------------------------------------------------------------- /docs/cbf/binaryadapter.md: -------------------------------------------------------------------------------- 1 | # BinaryAdapters 2 | 3 | Binary adapters are used to serialize and deserialize objects into binary data. 4 | 5 | ??? example "Default adapters" 6 | 7 | The following adapters are available by default: 8 | `Byte`, `ByteArray`, `Short`, `ShortArray`, `Int`, `IntArray`, `Long`, `LongArray`, `Float`, `FloatArray`, 9 | `Double`, `DoubleArray`, `Boolean`, `BooleanArray`, `Char`, `CharArray`, `String`, `Array`, 10 | `Enum`, `UUID`, `Pair`, `Triple`, `List`, `Set`, `Map`, `Compound` 11 | 12 | ## Creating your own adapter 13 | 14 | Creating your own adapter is easy. You just need to implement the `BinaryAdapter` interface and register it via the 15 | ``CBF.registerBinaryAdapter`` function. If you also want subclasses of a type to be serialized, you can use the 16 | ``CBF.registerBinaryHierarchyAdapter`` function. (For example, the `List` adapter) 17 | 18 | Let's create a simple adapter that serializes and deserializes a ``java.awt.Color`` instance. 19 | 20 | ```kotlin title="ColorAdapter" 21 | object ColorBinaryAdapter : BinaryAdapter { 22 | 23 | override fun read(type: Type, buf: ByteBuffer): Color { 24 | return Color(buf.readInt(), true) 25 | } 26 | 27 | override fun write(obj: Color, buf: ByteBuffer) { 28 | buf.writeInt(obj.rgb) 29 | } 30 | 31 | } 32 | ``` 33 | 34 | and register it: 35 | 36 | ```kotlin 37 | CBF.registerBinaryAdapter(Color::class, ColorBinaryAdapter) 38 | ``` 39 | 40 | If you have generic types, you can use the type parameter. 41 | 42 | ```kotlin title="PairBinaryAdapter" 43 | object PairBinaryAdapter : BinaryAdapter> { 44 | 45 | override fun write(obj: Pair<*, *>, buf: ByteBuffer) { 46 | CBF.write(obj.first, buf) 47 | CBF.write(obj.second, buf) 48 | } 49 | 50 | override fun read(type: Type, buf: ByteBuffer): Pair<*, *> { 51 | val typeArguments = (type as ParameterizedType).actualTypeArguments 52 | 53 | return Pair( 54 | CBF.read(typeArguments[0], buf), 55 | CBF.read(typeArguments[1], buf) 56 | ) 57 | } 58 | 59 | } 60 | ``` 61 | 62 | The registration works the same way: 63 | 64 | ```kotlin 65 | CBF.registerBinaryAdapter(Pair::class, PairBinaryAdapter) 66 | ``` 67 | 68 | !!! note 69 | 70 | Both these adapters are already registered by default. Replacing them might cause issues with already serialized data. -------------------------------------------------------------------------------- /docs/cbf/compound.md: -------------------------------------------------------------------------------- 1 | # Compound 2 | 3 | Compounds allow you store a lot of data in a single object. This makes it easier to get desired data on demand instead of 4 | continuously reading from a byte stream. You can pretty much see compounds as maps or json objects. Similar to json, data 5 | in compounds can be nested and is deserialized lazily. This means that until you specifically request a value, it will be 6 | stored as a byte array (It will of course be cached after the first time it is requested). Let's create a compound: 7 | 8 | ```kotlin 9 | val compound = Compound() 10 | compound["test1"] = 123 11 | compound["test2"] = "test" 12 | compound["test3"] = true 13 | compound["test4"] = mapOf("test" to 123, "test2" to 321) 14 | compound["test5"] = Compound().apply { this["list"] = listOf(123, 321) } 15 | ``` 16 | 17 | This will create a compound with the following data: 18 | 19 | ``` 20 | { 21 | "test1": 123 22 | "test2": test 23 | "test3": true 24 | "test4": {test=123, test2=321} 25 | "test5": { 26 | "list": [123, 321] 27 | } 28 | } 29 | ``` 30 | 31 | We can serialize it the same way we serialize any other object: 32 | 33 | ```kotlin 34 | val bytes = CBF.write(compound) 35 | ``` 36 | 37 | We can then deserialize it, and get back the same data: 38 | 39 | ```kotlin 40 | val compound = CBF.read(bytes)!! 41 | println(compound.get("test1")) // prints 123 42 | println(compound.get("test2")) // prints test 43 | println(compound.get("test3")) // prints true 44 | println(compound.get>("test4")) // prints {test=123, test2=321} 45 | println(compound.get("test5")!!.get>("list")) // prints [123, 321] 46 | ``` -------------------------------------------------------------------------------- /docs/cbf/index.md: -------------------------------------------------------------------------------- 1 | # Cosmic Binary Format 2 | 3 | ## Configuring Maven / Gradle 4 | 5 | CBF is xenondevs' lazy evaluation binary format used primarily in [Nova](https://github.com/xenondevs/Nova/). 6 | To use CBF, you first have to add the xenondevs maven repository to your build configuration. 7 | 8 | === "Maven" 9 | 10 | ```xml 11 | 12 | xenondevs 13 | https://repo.xenondevs.xyz/releases 14 | 15 | ``` 16 | 17 | === "Gradle Groovy" 18 | 19 | ```groovy 20 | maven { 21 | url 'https://repo.xenondevs.xyz/releases' 22 | } 23 | ``` 24 | 25 | === "Gradle Kotlin" 26 | 27 | ```kotlin 28 | maven { 29 | url = uri("https://repo.xenondevs.xyz/releases") 30 | } 31 | ``` 32 | 33 | Now you can add CBF to your build configuration: 34 | 35 | === "Maven" 36 | 37 | ```xml 38 | 39 | xyz.xenondevs.cbf 40 | cosmic-binary-format 41 | VERSION 42 | 43 | ``` 44 | 45 | === "Gradle Groovy" 46 | 47 | ```groovy 48 | implementation "xyz.xenondevs.cbf:cosmic-binary-format:VERSION" 49 | ``` 50 | 51 | === "Gradle Kotlin" 52 | 53 | ```kotlin 54 | implementation("xyz.xenondevs.cbf:cosmic-binary-format:VERSION") 55 | ``` 56 | -------------------------------------------------------------------------------- /docs/cbf/instancecreator.md: -------------------------------------------------------------------------------- 1 | # InstanceCreators 2 | 3 | Some serializable classes can't be initiated via an empty constructor. This is where ``InstanceCreators`` come in. For 4 | example, an ``EnumMap`` needs the enum class as a constructor parameter. So we can use the ``Type`` parameter retrieved 5 | when calling a serialization function to pass the enum class. 6 | 7 | ```kotlin title="EnumMapInstanceCreator" 8 | object EnumMapInstanceCreator : InstanceCreator> { 9 | 10 | override fun createInstance(type: KType): EnumMap<*, *> { 11 | val clazz = type.arguments[0].type!!.classifierClass!!.java 12 | return createEnumMap(clazz) 13 | } 14 | 15 | @Suppress("UNCHECKED_CAST") 16 | private fun > createEnumMap(clazz: Class<*>): EnumMap<*, *> { 17 | return EnumMap(clazz as Class) 18 | } 19 | 20 | } 21 | ``` 22 | 23 | You can register it similarly to [``BinaryAdapters``](binaryadapter.md). 24 | 25 | ```kotlin 26 | registerInstanceCreator(EnumMap::class, EnumMapInstanceCreator) 27 | ``` 28 | 29 | !!! note 30 | 31 | This instance creator is registered by default. -------------------------------------------------------------------------------- /docs/cbf/serialization.md: -------------------------------------------------------------------------------- 1 | ## Serialization 2 | 3 | To serialize objects, you can use `#!kotlin CBF.write`: 4 | 5 | ```kotlin 6 | val out = ByteArrayOutputStream() 7 | val writer = ByteWriter.fromStream(out) 8 | val list = listOf("test1", "test2", "test3") 9 | CBF.write(list, writer) 10 | val bytes = out.toByteArray() 11 | ``` 12 | 13 | or 14 | 15 | ```kotlin 16 | val list = listOf("test1", "test2", "test3") 17 | val bytes = CBF.write(list) 18 | ``` 19 | 20 | ## Deserialization 21 | 22 | To deserialize binary data, you can use `#!kotlin CBF.read`: 23 | 24 | ```kotlin 25 | val inp: InputStream // ... 26 | val reader = ByteReader.fromStream(inp) 27 | val list = CBF.read>(reader) 28 | ``` 29 | 30 | or 31 | 32 | ```kotlin 33 | val list = CBF.read>(bytes) 34 | ``` 35 | -------------------------------------------------------------------------------- /docs/invui.yml: -------------------------------------------------------------------------------- 1 | INHERIT: base.yml 2 | docs_dir: invui 3 | site_name: InvUI Documentation 4 | site_url: https://xenondevs.xyz/docs/invui/ 5 | edit_uri: edit/main/docs/invui/ 6 | 7 | theme: 8 | features: 9 | - navigation.footer 10 | - content.tabs.link 11 | - content.code.annotate 12 | - content.code.copy 13 | - content.code.select 14 | 15 | nav: 16 | - "Getting Started": "index.md" 17 | - "Basics": "basics.md" 18 | - "Items": "items.md" 19 | - "Structure": "structure.md" 20 | - "GUIs": 21 | - "Overview": "guis/index.md" 22 | - "Normal GUI": "guis/normal.md" 23 | - "Paged GUI": "guis/paged.md" 24 | - "Scroll GUI": "guis/scroll.md" 25 | - "Tab GUI": "guis/tab.md" 26 | - "Windows": "windows.md" 27 | - "Inventory": "inventory.md" 28 | - "Localization": "localization.md" 29 | -------------------------------------------------------------------------------- /docs/invui/assets/js/mathjax.js: -------------------------------------------------------------------------------- 1 | window.MathJax = { 2 | tex: { 3 | inlineMath: [["\\(", "\\)"]], 4 | displayMath: [["\\[", "\\]"]], 5 | processEscapes: true, 6 | processEnvironments: true 7 | }, 8 | options: { 9 | ignoreHtmlClass: ".*|", 10 | processHtmlClass: "arithmatex", 11 | enableMenu: false 12 | } 13 | }; 14 | 15 | document$.subscribe(() => { 16 | MathJax.typesetPromise() 17 | }) -------------------------------------------------------------------------------- /docs/invui/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xenondevs/Docs/59ad882f0f1c9eb9dc27ce136a68b70dfde63db7/docs/invui/assets/logo.png -------------------------------------------------------------------------------- /docs/invui/assets/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | /*noinspection CssUnresolvedCustomProperty*/ 2 | :root > * { 3 | --md-primary-fg-color: #1B1D1F; 4 | --md-primary-bg-color: #FFFFFF; 5 | --md-default-bg-color: #151719; 6 | --md-typeset-a-color: var(--md-code-hl-function-color); 7 | --md-default-fg-color--light: #abb9cb; 8 | 9 | --md-code-hl-color: hsla(163, 34%, 67%, 0.5); 10 | --md-code-hl-number-color: #0aa370; 11 | --md-code-hl-special-color: #73a0d3; 12 | --md-code-hl-function-color: #7eb6f6; 13 | --md-code-hl-constant-color: #abb9cb; 14 | --md-code-hl-keyword-color: #47ebb4; 15 | --md-code-hl-string-color: #7eb6f6; 16 | --md-code-hl-name-color: var(--md-code-fg-color); 17 | --md-code-hl-operator-color: #47ebb4; 18 | --md-code-hl-punctuation-color: #abb9cb; 19 | --md-code-hl-comment-color: #abb9cb; 20 | --md-code-hl-generic-color: var(--md-default-fg-color--light); 21 | --md-code-hl-variable-color: #7eb6f6; 22 | } 23 | 24 | @media only screen and (min-width: 76.25em) { 25 | .md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link:not(.md-nav__container) { 26 | width: 90%; 27 | } 28 | 29 | .md-main__inner { 30 | max-width: none; 31 | } 32 | 33 | .md-content { 34 | max-width: 56.5%; 35 | } 36 | 37 | .md-sidebar--primary { 38 | margin-left: 3.75%; 39 | margin-right: 3.75%; 40 | } 41 | 42 | .md-sidebar--secondary { 43 | margin-left: 3.75%; 44 | margin-right: 3.75%; 45 | -webkit-transform: none; 46 | transform: none; 47 | } 48 | 49 | body::-webkit-scrollbar { 50 | width: 15px; 51 | height: 15px; 52 | } 53 | 54 | body::-webkit-scrollbar-thumb { 55 | background-color: #21222c; 56 | border-radius: 15px; 57 | } 58 | 59 | body::-webkit-scrollbar-track { 60 | background-color: #2e303e; 61 | } 62 | 63 | body { 64 | scrollbar-color: #21222c #2e303e; 65 | } 66 | 67 | } 68 | 69 | .md-nav { 70 | font-size: 14px; 71 | line-height: 1.4; 72 | } 73 | 74 | .md-nav__link { 75 | display: inline-flex; 76 | } 77 | 78 | .md-typeset { 79 | font-size: .7rem; 80 | line-height: 1.5; 81 | } 82 | 83 | .md-typeset :not(pre) > code { 84 | background-color: #23262b; 85 | } 86 | 87 | .text-center { 88 | text-align: center !important; 89 | } 90 | 91 | .md-tabs { 92 | box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 0.2rem, rgba(0, 0, 0, 0.2) 0px 0.2rem 0.4rem; 93 | transition: transform 0.25s cubic-bezier(0.1, 0.7, 0.1, 1) 0s, box-shadow 0.25s ease 0s; 94 | } 95 | 96 | .md-nav__item .md-nav__link--active { 97 | color: #768390; 98 | } 99 | 100 | .md-typeset .admonition { 101 | background-color: var(--md-primary-fg-color); 102 | border-width: 0; 103 | border-left-width: 4px; 104 | } 105 | 106 | .md-typeset details { 107 | background-color: var(--md-primary-fg-color); 108 | border-width: 0; 109 | border-left-width: 4px; 110 | } 111 | 112 | .md-typeset table:not([class]) { 113 | border-width: 0; 114 | box-shadow: 0 0.2rem 0.5rem hsla(0, 0%, 0%, 0.3),0 0 0.05rem hsla(0, 0%, 0%, 0.2); 115 | border-radius: 5px; 116 | } 117 | 118 | .md-typeset th { 119 | background-color: #111111; 120 | } 121 | 122 | .md-typeset td { 123 | background-color: #1b1d1f; 124 | } 125 | 126 | .md-typeset tbody tr:first-child td { 127 | border-color: #3593ff; 128 | } 129 | 130 | .md-footer { 131 | background-color: var(--md-primary-fg-color); 132 | } 133 | 134 | .md-footer-meta { 135 | background: none; 136 | } 137 | 138 | .md-typeset .md-button { 139 | color: #fff; 140 | } 141 | 142 | .md-typeset .md-button:hover { 143 | background-color: rgba(255, 255, 255, 0.1); 144 | border-color: rgba(255, 255, 255, 0); 145 | } -------------------------------------------------------------------------------- /docs/invui/guis/index.md: -------------------------------------------------------------------------------- 1 | ## What is a GUI in InvUI? 2 | 3 | A GUI is basically a container for width * height SlotElements. 4 | Each SlotElement can either be an Item, a reference to another GUI or a reference to a VirtualInventory. 5 | 6 | Items and other SlotElements can be added using `gui.setItem(x, y, item);`, `gui.setItem(index, item);` or by using a Structure and applying that to the GUI `gui.applyStructure(structure);`. 7 | 8 | GUIs cannot display anything to a player, a Window is used for that. 9 | 10 | ## Different types of GUIs 11 | 12 | There are four different GUI types available: 13 | 14 | | Gui Type | Builder Factory Function(s) | Description | 15 | |-------------------------|-----------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 16 | | [Normal GUI](normal.md) | `#!java Gui.normal()` | A normal GUI without any special functionality. | 17 | | [Paged GUI](paged.md) | `#!java PagedGui.items()`, `#!java PagedGui.guis()` | A GUI with paging functionality. You can either provide a list of [`Items`](../items.md) that should be shown on the pages or directly provide the pages (as `Guis`) themselves. | 18 | | [Scroll GUI](scroll.md) | `#!java ScrollGui.items()`, `#!java ScrollGui.guis()`, `#!java ScrollGui.inventories()` | A GUI with scrolling functionality. You can scroll through [`Items`](../items.md), `Guis` or [`Inventories`](../inventory.md) | 19 | | [Tab GUI](tab.md) | `#!java TabGui.normal()` | A GUI with tabs. Each tab is associated to a `Gui`, you can switch between tabs using tab items. | 20 | 21 | ## Animations 22 | 23 | GUIs can also play animations with `#!java gui.playAnimation(animation);`. 24 | While an animation is running, the player can't interact with any Item in the GUI. 25 | 26 | 27 | Available Animations: 28 | 29 | * SequentialAnimation 30 | * SplitSequentialAnimation 31 | * HorizontalSnakeAnimation 32 | * VerticalSnakeAnimation 33 | * RowAnimation 34 | * ColumnAnimation 35 | * RandomAnimation 36 | 37 | To cancel a running animation, use `#!java gui.cancelAnimation();` 38 | 39 | ## Filling methods 40 | 41 | There are also some utility methods for filling available: 42 | 43 | === "Kotlin" 44 | 45 | ```kotlin 46 | gui.fill(item, replace) // fills everything 47 | gui.fill(start, end, item, replace) // fills from the start index to the end index 48 | gui.fillColumn(column, item, replace) // fills a column 49 | gui.fillRow(row, item, replace) // fills a row 50 | gui.fillBorders(item, replace) // fill the borders 51 | gui.fillRectangle(x, y, width, height, item, replace) // fills a rectangle with an Item 52 | 53 | gui.fillRectangle(x, y, gui, replace) // fills a rectangle with another GUI 54 | gui.fillRectangle(x, y, width, virtualInventory, replace) // fills a rectangle with a VirtualInventory 55 | ``` 56 | 57 | === "Java" 58 | 59 | ```java 60 | gui.fill(item, replace); // fills everything 61 | gui.fill(start, end, item, replace); // fills from the start index to the end index 62 | gui.fillColumn(column, item, replace); // fills a column 63 | gui.fillRow(row, item, replace); // fills a row 64 | gui.fillBorders(item, replace); // fill the borders 65 | gui.fillRectangle(x, y, width, height, item, replace); // fills a rectangle with an Item 66 | 67 | gui.fillRectangle(x, y, gui, replace); // fills a rectangle with another GUI 68 | gui.fillRectangle(x, y, width, virtualInventory, replace); // fills a rectangle with a VirtualInventory 69 | ``` 70 | 71 | ## Background Item 72 | 73 | Every GUI can also have an ``ItemProvider`` as the background. This background Item will be shown when there is nothing occupying that slot. It can be useful for dealing with paged GUIs if you don't want any blank spots in your inventory. 74 | You can set the background via: 75 | 76 | === "Kotlin" 77 | 78 | ```kotlin 79 | gui.setBackground(itemProvider) 80 | ``` 81 | 82 | === "Java" 83 | 84 | ```java 85 | gui.setBackground(itemProvider); 86 | ``` -------------------------------------------------------------------------------- /docs/invui/guis/normal.md: -------------------------------------------------------------------------------- 1 | Just a normal GUI without any special functionality. 2 | This example shows how to create one using the GuiBuilder: 3 | 4 | === "Kotlin" 5 | 6 | ```kotlin 7 | val border = SimpleItem(ItemBuilder(Material.BLACK_STAINED_GLASS_PANE)) 8 | val gui = Gui.normal() 9 | .setStructure( 10 | "# # # # # # # # #", 11 | "# . . . . . . . #", 12 | "# . . . . . . . #", 13 | "# # # # # # # # #") 14 | .addIngredient('#', border) 15 | .build() 16 | ``` 17 | 18 | === "Java" 19 | 20 | ```java 21 | Item border = new SimpleItem(new ItemBuilder(Material.BLACK_STAINED_GLASS_PANE)); 22 | Gui gui = Gui.normal() 23 | .setStructure( 24 | "# # # # # # # # #", 25 | "# . . . . . . . #", 26 | "# . . . . . . . #", 27 | "# # # # # # # # #") 28 | .addIngredient('#', border) 29 | .build(); 30 | ``` 31 | 32 | ![](https://i.imgur.com/MZmFbnJ.png) -------------------------------------------------------------------------------- /docs/invui/guis/tab.md: -------------------------------------------------------------------------------- 1 | # Tab GUI 2 | 3 | A Tab GUI lets you switch between different GUIs (tabs) by clicking the Tab Items associated with them. 4 | 5 | ## Control Items 6 | 7 | First, we'll need to create our Tab Items: 8 | 9 | === "Kotlin" 10 | 11 | ```kotlin 12 | class MyTabItem(private val tab: Int) : TabItem(tab) { 13 | 14 | override fun getItemProvider(gui: TabGui): ItemProvider { 15 | return if (gui.currentTab == tab) { 16 | ItemBuilder(Material.GLOWSTONE_DUST) 17 | .setDisplayName("Tab $tab (selected)") 18 | } else { 19 | ItemBuilder(Material.GUNPOWDER) 20 | .setDisplayName("Tab $tab (not selected)") 21 | } 22 | } 23 | 24 | } 25 | ``` 26 | 27 | === "Java" 28 | 29 | ```java 30 | public class MyTabItem extends TabItem { 31 | 32 | private final int tab; 33 | 34 | public MyTabItem(int tab) { 35 | super(tab); 36 | this.tab = tab; 37 | } 38 | 39 | @Override 40 | public ItemProvider getItemProvider(TabGui gui) { 41 | if (gui.getCurrentTab() == tab) { 42 | return new ItemBuilder(Material.GLOWSTONE_DUST) 43 | .setDisplayName("Tab " + tab + " (selected)"); 44 | } else { 45 | return new ItemBuilder(Material.GUNPOWDER) 46 | .setDisplayName("Tab " + tab + " (not selected)"); 47 | } 48 | } 49 | 50 | } 51 | ``` 52 | 53 | ## Creating the GUI 54 | 55 | Now, lets create the actual TabGui. In this example, I've only created two tabs, but you can have as many tabs as you want. 56 | 57 | === "Kotlin" 58 | 59 | ```kotlin 60 | val border = SimpleItem(ItemBuilder(Material.BLACK_STAINED_GLASS_PANE).setDisplayName("")) 61 | 62 | val gui1 = Guis.empty(9, 3) 63 | gui1.fill(SimpleItem(ItemBuilder(Material.DIRT)), true) 64 | 65 | val gui2 = Guis.empty(9, 3) 66 | gui2.fill(SimpleItem(ItemBuilder(Material.DIAMOND)), true) 67 | 68 | val gui = TabGui.normal() 69 | .setStructure( 70 | "# # # 0 # 1 # # #", 71 | "x x x x x x x x x", 72 | "x x x x x x x x x", 73 | "x x x x x x x x x") 74 | .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) 75 | .addIngredient('#', border) 76 | .addIngredient('0', MyTabItem(0)) 77 | .addIngredient('1', MyTabItem(1)) 78 | .setTabs(listOf(gui1, gui2)) 79 | .build() 80 | ``` 81 | 82 | === "Java" 83 | 84 | ```java 85 | Item border = new SimpleItem(new ItemBuilder(Material.BLACK_STAINED_GLASS_PANE).setDisplayName("")); 86 | 87 | Gui gui1 = Guis.empty(9, 3); 88 | gui1.fill(new SimpleItem(new ItemBuilder(Material.DIRT)), true); 89 | 90 | Gui gui2 = Guis.empty(9, 3); 91 | gui2.fill(new SimpleItem(new ItemBuilder(Material.DIAMOND)), true); 92 | 93 | Gui gui = TabGui.normal() 94 | .setStructure( 95 | "# # # 0 # 1 # # #", 96 | "x x x x x x x x x", 97 | "x x x x x x x x x", 98 | "x x x x x x x x x") 99 | .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) 100 | .addIngredient('#', border) 101 | .addIngredient('0', new MyTabItem(0)) 102 | .addIngredient('1', new MyTabItem(1)) 103 | .setTabs(Arrays.asList(gui1, gui2)) 104 | .build(); 105 | ``` 106 | 107 | And this is how it looks like: 108 | ![](https://i.imgur.com/tO4Rc06.gif){ align=center } 109 | 110 | !!! info 111 | 112 | It is also possible to, for example, put the Tab Items in a separate paged or scroll gui if you need more space. 113 | For that to work, you'd need to set the TabGUI that the Tab items are supposed to control manually using `ControlItem#setGui`. 114 | -------------------------------------------------------------------------------- /docs/invui/localization.md: -------------------------------------------------------------------------------- 1 | # Localization 2 | 3 | InvUI makes it easy for you to translate your GUIs into different languages. 4 | 5 | ## Adding translations 6 | 7 | To add translations for a new language, you can call `#!java Languages.getInstance().addLanguage(String lang, Map translations)`. 8 | The `lang` parameter is the language code of the language you want to add. The `translations` parameter should be a 9 | map containing the translation keys and their translations. 10 | 11 | If your translations are stored in a json structure like this: 12 | 13 | ```json title="en_us.json" 14 | { 15 | "translation.key": "Hello World", 16 | "other.translation.key": "Hello World 2" 17 | } 18 | ``` 19 | 20 | You can also load them using `Languages#loadLanguage`: 21 | 22 | === "Kotlin" 23 | 24 | ```kotlin 25 | Languages.getInstance().loadLanguage("en_us", File("en_us.json"), Charsets.UTF_8) 26 | ``` 27 | 28 | === "Java" 29 | 30 | ```java 31 | Languages.getInstance().loadLanguage("en_us", new File("en_us.json"), StandardCharsets.UTF_8); 32 | ``` 33 | 34 | ## Specifying Player language 35 | 36 | By default, the player's locale is retrieved using `#!java Player.getLocale();`. If you want to change this behavior, you can 37 | do so by calling `Languages#setLanguageProvider`: 38 | 39 | === "Kotlin" 40 | 41 | ```kotlin 42 | Languages.getInstance().setLanguageProvider(Player::getLocale) 43 | ``` 44 | 45 | === "Java" 46 | 47 | ```java 48 | Languages.getInstance().setLanguageProvider(Player::getLocale); 49 | ``` 50 | 51 | ## Using translations 52 | 53 | To use translations in your GUIs, simply use translatable components using either md_5's bungee component api or kyori's 54 | adventure text api. If you're not using Kotlin and the `invui-kotlin` module, you'll need to wrap adventure components 55 | using the `AdventureComponentWrapper` class before you can pass them to the `ItemBuilder` or `Window.Builder`. 56 | 57 | !!! question "When are translations applied?" 58 | 59 | Translations are applied server-side. This is done during `#!java ItemBuilder.get();` or, for the inventory title, `#!java Window.open();`. 60 | If you need to use server-side translations in other places, you'll need to manually call `#!java ComponentWrapper.localized(String locale);`. 61 | 62 | ## Disabling server-side translations 63 | 64 | If you do not want your items and GUIs to be translated server-side and instead want to actually send translatable 65 | components to your players, you can disable server-side translations by calling `#!java Languages.getInstance().enableServerSideTranslations(false);`. -------------------------------------------------------------------------------- /docs/invui/structure.md: -------------------------------------------------------------------------------- 1 | # Structure 2 | 3 | ## Creating and using Structures 4 | 5 | Inspired by Bukkit's ShapedRecipe, a Structure will let you design the layout of the GUI in a similar way. 6 | Each ingredient is associated with a character and you "draw" the GUI in your code. 7 | 8 | This is an example I stole from the [Paged GUI](guis/paged.md) section: 9 | 10 | === "Kotlin" 11 | 12 | ```kotlin 13 | // create a structure 14 | val structure = Structure( 15 | "# # # # # # # # #", 16 | "# x x x x x x x #", 17 | "# x x x x x x x #", 18 | "# # # < # > # # #") 19 | .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) // where paged items should be put 20 | .addIngredient('#', ItemBuilder(Material.BLACK_STAINED_GLASS_PANE).setDisplayName("")) // this will just create a SimpleItem with the given ItemBuilder 21 | .addIngredient('<', BackItem()) 22 | .addIngredient('>', ForwardItem()) 23 | 24 | // use it in a GUI Builder 25 | PagedGui.items().setStructure(structure) 26 | 27 | // or use it on an existing GUI 28 | gui.applyStructure(structure) 29 | ``` 30 | 31 | === "Java" 32 | 33 | ```java 34 | // create a structure 35 | Structure structure = new Structure( 36 | "# # # # # # # # #", 37 | "# x x x x x x x #", 38 | "# x x x x x x x #", 39 | "# # # < # > # # #") 40 | .addIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) // where paged items should be put 41 | .addIngredient('#', new ItemBuilder(Material.BLACK_STAINED_GLASS_PANE).setDisplayName("")) // this will just create a SimpleItem with the given ItemBuilder 42 | .addIngredient('<', new BackItem()) 43 | .addIngredient('>', new ForwardItem()); 44 | 45 | // use it in a GUI Builder 46 | PagedGui.items().setStructure(structure); 47 | 48 | // or use it on an existing GUI 49 | gui.applyStructure(structure); 50 | ``` 51 | 52 | As you can see, there are many types of ingredients that can be added: 53 | `Items`, `Item Supplier`, `SlotElement`, `SlotElement Supplier`, `VirtualInventory`, `Marker`. 54 | When using an `Item` or `SlotElement as ingredient, the same instance will be used on every slot. 55 | If you don't want this, use an Item Supplier. This will create a new instance for every slot. 56 | 57 | ## Global Ingredients 58 | 59 | If you're reusing the same ingredients over and over again, consider registering them as global ingredients. 60 | Make sure to use suppliers for every item where you want a new instance per slot. 61 | 62 | 63 | === "Kotlin" 64 | 65 | ```kotlin 66 | // Supplier is not needed here as the Item does not do anything 67 | Structure.addGlobalIngredient('#', ItemBuilder(Material.BLACK_STAINED_GLASS_PANE).setDisplayName("")) 68 | 69 | // These items need a supplier 70 | Structure.addGlobalIngredient('<', ::BackItem) 71 | Structure.addGlobalIngredient('>', ::ForwardItem) 72 | 73 | // Adding the Markers.CONTENT_LIST_SLOT_HORIZONTAL as a global ingredient is also a good idea 74 | Structure.addGlobalIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL) 75 | ``` 76 | 77 | === "Java" 78 | 79 | ```java 80 | // Supplier is not needed here as the Item does not do anything 81 | Structure.addGlobalIngredient('#', new ItemBuilder(Material.BLACK_STAINED_GLASS_PANE).setDisplayName("")); 82 | 83 | // These items need a supplier because ControlItems can only control one GUI 84 | Structure.addGlobalIngredient('<', BackItem::new); 85 | Structure.addGlobalIngredient('>', ForwardItem::new); 86 | 87 | // Adding the Markers.CONTENT_LIST_SLOT_HORIZONTAL as a global ingredient is also a good idea 88 | Structure.addGlobalIngredient('x', Markers.CONTENT_LIST_SLOT_HORIZONTAL); 89 | ``` -------------------------------------------------------------------------------- /docs/nova/addon/abilities.md: -------------------------------------------------------------------------------- 1 | # Abilities 2 | 3 | ## What are Abilities? 4 | 5 | In Nova, abilities are ticking objects that can be assigned to players. They work similar to [attachments](attachments.md), 6 | except that they have no logic by default. 7 | 8 | ## Creating your own Ability 9 | 10 | To create your own ability, inherit from `Ability` and add your custom `handleRemoved` and `handleTick` logic. 11 | 12 | ```kotlin 13 | class ExampleAbility(player: Player) : Ability(player) { 14 | 15 | override fun handleRemove() { 16 | // TODO 17 | } 18 | 19 | override fun handleTick() { 20 | // TODO 21 | } 22 | 23 | } 24 | ``` 25 | 26 | Then, register a new ability type for that ability: 27 | ```kotlin 28 | @Init(stage = InitStage.PRE_PACK) 29 | object Abilities { 30 | 31 | val EXAMPLE_ABILITY = ExampleAddon.registerAbilityType("example_ability", ::MyAbility) 32 | 33 | } 34 | ``` 35 | 36 | Then, give the ability to a player: 37 | 38 | ```kotlin 39 | AbilityManager.giveAbility(player, Abilities.EXAMPLE_ABILITY) 40 | ``` 41 | 42 | And this is how you remove an ability: 43 | 44 | ```kotlin 45 | AbilityManager.takeAbility(player, Abilities.EXAMPLE_ABILITY) 46 | ``` -------------------------------------------------------------------------------- /docs/nova/addon/addon-configuration.md: -------------------------------------------------------------------------------- 1 | # Configure Addon 2 | 3 | Now that you've created your project, you need to set several values in the `addon` task. 4 | :material-information-outline:{ title="Part of the xyz.xenondevs.nova:nova-gradle-plugin" } 5 | If you're using the addon template, most of these values are already set for you. 6 | 7 | **Options marked with a * are required.** 8 | 9 | ## name* 10 | 11 | This is the name of your addon. 12 | Names may only contain alphanumeric characters, periods, underscores, and hyphens (`[A-Za-z0-9._-]+`). 13 | The lowercase version of this name is used as your addon's id. 14 | Your items and blocks are linked to this id, so you cannot change it later, without breaking existing worlds. 15 | 16 | Example: 17 | 18 | ```kotlin title="build.gradle.kts addon { }" 19 | name.set("example") 20 | ``` 21 | 22 | In most cases, you can just use your project name: 23 | 24 | ```kotlin title="build.gradle.kts addon { }" 25 | name.set(project.name) 26 | ``` 27 | 28 | ## version* 29 | 30 | The version of the addon. 31 | 32 | Example: 33 | 34 | ```kotlin title="build.gradle.kts addon { }" 35 | version.set("1.0") 36 | ``` 37 | 38 | Or to automatically get the version from your project: 39 | 40 | ```kotlin title="build.gradle.kts addon { }" 41 | version.set(project.version.toString()) 42 | ``` 43 | 44 | ## main* 45 | 46 | Full path to your addon main class (without the .class extension). 47 | 48 | Example: 49 | 50 | ```kotlin title="build.gradle.kts addon { }" 51 | main.set("com.example.ExampleAddon") 52 | ``` 53 | 54 | ## dependency 55 | 56 | You can declare dependencies on other plugins / addons using `dependency`: 57 | 58 | Example: 59 | 60 | The following creates a dependency in both `BOOTSTRAP` and `SERVER` phase on the given plugin: 61 | (If you're depending on a Nova addon, do this) 62 | ```kotlin title="build.gradle.kts addon { }" 63 | dependency("machines") 64 | ``` 65 | 66 | For plugins that don't have a bootstrapper, you can instead specify the phase: 67 | ```kotlin title="build.gradle.kts addon { }" 68 | dependency("some-plugin", PluginDependency.Stage.SERVER) 69 | ``` 70 | 71 | ## pluginMain 72 | 73 | Full path to your plugin main class (without the .class extension). 74 | If you don't define this property, Nova will generate a plugin main class for you. 75 | You will be able to access your plugin instance via your addon object. 76 | 77 | Example: 78 | 79 | ```kotlin title="build.gradle.kts addon { }" 80 | pluginMain.set("com.example.ExamplePlugin") 81 | ``` 82 | 83 | ## loader 84 | 85 | A custom [plugin loader](https://docs.papermc.io/paper/dev/getting-started/paper-plugins#loaders). 86 | Defining a custom plugin loader will disable Nova's library loading mechanism that can be 87 | accessed via the `libraryLoader` dependency configuration. 88 | 89 | Example: 90 | 91 | ```kotlin title="build.gradle.kts addon { }" 92 | loader.set("com.example.ExampleLoader") 93 | ``` 94 | 95 | ## bootstrapper 96 | 97 | A custom [bootstrapper](https://docs.papermc.io/paper/dev/getting-started/paper-plugins#bootstrapper). 98 | 99 | Example: 100 | 101 | ```kotlin title="build.gradle.kts addon { }" 102 | bootstrapper.set("com.example.ExampleBootstrapper") 103 | ``` 104 | 105 | ## description 106 | 107 | A description of your addon. 108 | 109 | Example: 110 | 111 | ```kotlin title="build.gradle.kts addon { }" 112 | description.set("This is an example addon.") 113 | ``` 114 | 115 | ## authors 116 | 117 | A list of author(s) of your addon. 118 | 119 | Example: 120 | 121 | ```kotlin title="build.gradle.kts addon { }" 122 | authors.add("ExampleAuthor") 123 | ``` 124 | 125 | ## contributors 126 | 127 | A list of contributors to your addon. 128 | 129 | Example: 130 | 131 | ```kotlin title="build.gradle.kts addon { }" 132 | contributors.add("ExampleContributor") 133 | ``` 134 | 135 | Or for multiple authors: 136 | 137 | ```kotlin title="build.gradle.kts addon { }" 138 | authors.set(listOf("ExampleAuthor", "Another Author")) 139 | ``` 140 | 141 | ## website 142 | 143 | A website for your addon. 144 | 145 | Example: 146 | 147 | ```kotlin title="build.gradle.kts addon { }" 148 | website.set("https://example.com") 149 | ``` 150 | 151 | ## prefix 152 | 153 | The prefix used in log messages. 154 | 155 | Example: 156 | 157 | ```kotlin title="build.gradle.kts addon { }" 158 | prefix.set("example") 159 | ``` 160 | 161 | ## Example configuration 162 | 163 | ```kotlin title="build.gradle.kts" 164 | addon { 165 | name.set("ExampleAddon") 166 | version.set("0.1") 167 | main.set("com.example.ExampleAddon") 168 | authors.set(listOf("Example Author", "Another Author")) 169 | dependency("machines") 170 | } 171 | ``` -------------------------------------------------------------------------------- /docs/nova/addon/attachments.md: -------------------------------------------------------------------------------- 1 | # Attachments 2 | 3 | ## What is an Attachment? 4 | 5 | In Nova, an `Attachment` is a (fake) entity riding a player, but you'll mostly likely use the `ItemAttachment` 6 | implementation which is a clientside armor stand riding a player while wearing a custom item as a helmet. 7 | The attachment system makes assigning models to players easy, as it handles all the riding logic. 8 | Once a player is given an attachment, it will stay on that player until it is removed again. 9 | 10 | ## Creating an AttachmentType 11 | 12 | Register an attachment type: 13 | 14 | ```kotlin 15 | @Init(stage = InitStage.PRE_PACK) 16 | object Attachments { 17 | 18 | val EXAMPLE_ATTACHMENT = ExampleAddon.registerAttachmentType("example_attachment") { ItemAttachment(it, Items.ATTACHMENT_ITEM) } 19 | 20 | } 21 | ``` 22 | 23 | Then, add the attachment to a player: 24 | 25 | ```kotlin 26 | AttachmentManager.addAttachment(player, Attachments.EXAMPLE_ATTACHMENT) 27 | ``` 28 | 29 | And this is how you would remove it again: 30 | 31 | ```kotlin 32 | AttachmentManager.removeAttachment(player, Attachments.EXAMPLE_ATTACHMENT) 33 | ``` 34 | 35 | ## Modifying Attachment Logic 36 | 37 | You can also change the attachment logic by either directly implementing the `Attachment` interface or by extending the 38 | `ItemAttachment` class. Then, just call the constructor of your 'Attachment' subclass when registering the attachment type. 39 | 40 | If you need an attachment which gets hidden when a player looks down, check out the `HideOnDownItemAttachment`. -------------------------------------------------------------------------------- /docs/nova/addon/blocks/block-behaviors.md: -------------------------------------------------------------------------------- 1 | Similar to [ItemBehaviors](../items/item-behaviors.md), you can implement block logic via `BlockBehavior`. 2 | 3 | ```kotlin 4 | @Init(stage = InitStage.PRE_PACK) 5 | object Blocks { 6 | 7 | val EXAMPLE_BLOCK = ExampleAddon.block("example_block") { 8 | behaviors(/*...*/) 9 | } 10 | 11 | } 12 | ``` 13 | 14 | ## Default block behaviors 15 | 16 | For a list of all available default block behaviors, refer to the 17 | [KDocs](https://nova.dokka.xenondevs.xyz/nova/xyz.xenondevs.nova.world.block.behavior/index.html) 18 | 19 | ## Custom Block Behavior 20 | 21 | To create a custom block behavior, you just need to implement the `BlockBehavior` interface and override the desired 22 | event methods. 23 | 24 | ### Random ticks 25 | 26 | To receive random ticks in your block behavior, make sure to also override `#!kotlin BlockBehavior.ticksRandomly` 27 | which is used to define what block states can receive random ticks. 28 | 29 | Every tick, every chunk section (16x16x16 blocks) will have `random-tick-speed` blocks chosen randomly to receive a 30 | random tick. 31 | -------------------------------------------------------------------------------- /docs/nova/addon/configs.md: -------------------------------------------------------------------------------- 1 | # Configs 2 | 3 | ## Configuration Library 4 | Nova uses [SpongePowered/Configurate](https://github.com/SpongePowered/Configurate), but most of the 5 | time you'll be dealing with Nova's `Provider`, which helps you with config reloading. 6 | 7 | ## Provider 8 | 9 | To help with config reloading, Nova offers the `Provider` class. 10 | Config providers are automatically reloaded when the config is reloaded. 11 | You can also chain value modification calls on a `Provider`. Those modification steps will then be run lazily every time 12 | the config is reloaded. 13 | 14 | Some of those modification functions are: 15 | 16 | * `map` - Maps the value to a new value. 17 | * `orElse` - Falls back to a default value if the value is null. 18 | 19 | Every time such a modification function is called, a new `Provider` is created and returned. This allows you to create 20 | several modified versions from the same `Provider`. 21 | 22 | You might also be interested in these `Provider`-related top-level functions: 23 | 24 | * `#!kotlin provider(value: T)` - Creates a constant `Provider` from a given value. 25 | * `#!kotlin provider(lazyValue: () -> T)` - Creates a `Provider` that loads it value lazily. 26 | * `combinedProvider()` - Creates a `#!kotlin Provider>` from a list of `#!kotlin Provider`s. 27 | 28 | ## Config extraction 29 | Nova will automatically extract all configs from `resources/configs/` on startup. 30 | New or changed keys will automatically be added / updated on the server as well, unless they have been modified on the server. 31 | 32 | ## Accessing configs 33 | To access the configs, retrieve them from `Configs`. 34 | You can either use their names: 35 | ```kotlin 36 | Configs["example:ruby"] // namespace:name (drop the .yml) 37 | ``` 38 | 39 | Or retrieve the config of a `NovaItem` or `NovaBlock` using `#!kotlin NovaItem.config` and `#!kotlin NovaBlock.config`. 40 | 41 | All of the above ways will result in you obtaining a config provider, which is a 42 | `#!kotlin Provider`. (Note that Configurate does not have support for yaml comments yet, 43 | but this should not be an issue unless you need to write to the config file.) 44 | Now, you can either retrieve the raw config node using `#!kotlin provider.get()`, or get a reloadable entry provider 45 | using `#!kotlin provider.entry("path")` and `#!kotlin provider.optionalEntry("path")`. 46 | 47 | ```kotlin 48 | val exampleValue1: Int by Items.EXAMPLE_ITEM.config.entry("example_value") // (1)! 49 | val exampleValue2: Int? by Items.EXAMPLE_ITEM.config.optionalEntry("optional_value") // (2)! 50 | ``` 51 | 52 | 1. Delegating to the `Provider` will cause this field automatically change every time the config is reloaded. 53 | 2. Using `Provider#optionalEntry`, you can get a `Provider`, where the value is null if the key 54 | is not present in the config. -------------------------------------------------------------------------------- /docs/nova/addon/contexts.md: -------------------------------------------------------------------------------- 1 | !!! info "Usage of Contexts in Nova" 2 | 3 | Contexts are currently only used for block-related events, but it is planned to expand them 4 | to other aspects in the future. 5 | 6 | A `Context`, at its core, is a simple key-value storage for `#!kotlin (ContextParamType, T)` pairs and 7 | a `ContextIntention`, which defines the allowed parameters. 8 | 9 | You can find a list of default context intentions [here](https://nova.dokka.xenondevs.xyz/nova/xyz.xenondevs.nova.context.intention/-default-context-intentions/index.html). 10 | You can find a list of default context param types [here](https://nova.dokka.xenondevs.xyz/nova/xyz.xenondevs.nova.context.param/-default-context-param-types/index.html). 11 | The documentation also notes which types are parameter types are required for which `ContextIntention` and which are optional. 12 | 13 | The context system can also infer parameters from other parameters, for example, you don't need to provide a 14 | `BLOCK_WORLD` parameter if you provide a `BLOCK_POS` parameter, and you can still read `BLOCK_WORLD` from the context. 15 | Which parameters are inferred from other parameters is also noted in the context param type documentation. 16 | 17 | ## Creating and using a Context 18 | 19 | And this is how you create and use a `Context`: 20 | 21 | ```kotlin 22 | // create a context to place EXAMPLE_BLOCK at pos 23 | val context = Context.intention(DefaultContextIntentions.BlockPlace) 24 | .param(DefaultContextParamTypes.BLOCK_POS, pos) 25 | .param(DefaultContextParamTypes.BLOCK_TYPE_NOVA, Blocks.EXAMPLE_BLOCK) 26 | .build() 27 | 28 | // read BLOCK_WORLD from context, which is inferred from BLOCK_POS 29 | val world: World = context.getOrThrow(DefaultContextParamTypes.BLOCK_WORLD) 30 | 31 | // place block 32 | BlockUtils.placeBlock(context) 33 | ``` 34 | 35 | ## Custom ContextParamType 36 | 37 | In the example below I've created a custom `ContextParamType` that can be used in the 38 | `BlockPlace`, `BlockBreak`, `BlockInteract` default intentions. It represents the name of the target block 39 | and is autofilled through either the `BLOCK_TYPE_NOVA` or `BLOCK_TYPE_VANILLA` parameter. 40 | 41 | ```kotlin 42 | val BLOCK_NAME: ContextParamType = 43 | ContextParamType.builder(Machines, "block_name") 44 | .optionalIn(BlockPlace, BlockBreak, BlockInteract) 45 | .autofilledBy(DefaultContextParamTypes::BLOCK_TYPE_NOVA) { type: NovaBlock -> type.name } 46 | .autofilledBy(DefaultContextParamTypes::BLOCK_TYPE_VANILLA) { type: Material -> Component.translatable(type.blockTranslationKey!!) } 47 | .build() 48 | ``` -------------------------------------------------------------------------------- /docs/nova/addon/entity-variants.md: -------------------------------------------------------------------------------- 1 | ## What is an Entity Variant? 2 | 3 | Entity variants are a vanilla Minecraft feature and allow you to add retextured variants of existing entities to the game. 4 | ([Minecraft Wiki](https://minecraft.wiki/w/Mob_variant)) 5 | 6 | ## Registering a custom entity variant 7 | You can register a custom entity variant like this: 8 | 9 | ```kotlin 10 | @Init(stage = InitStage.PRE_PACK) // (1)! 11 | object EntityVariants { 12 | 13 | val BLUE_COW by ExampleAddon.cowVariant("blue") { 14 | spawnConditions { 15 | // (2)! 16 | } 17 | 18 | texture(CowModelType.WARM /*(3)!*/) { 19 | texture("entity/cow/blue") 20 | } 21 | } 22 | 23 | } 24 | ``` 25 | 26 | 1. Nova will load this class during addon initialization, causing your variants to be registered. 27 | 2. The conditions for your custom variant to be selected when spawning. See [Minecraft Wiki - Spawn Conditions](https://minecraft.wiki/w/Mob_spawning#Spawn_conditions) 28 | for more information. 29 | 3. Some entities offer multiple model types. For example, the warm cow has horns. You can select the model type here. -------------------------------------------------------------------------------- /docs/nova/addon/fonts/actionbar.md: -------------------------------------------------------------------------------- 1 | Overlays follow the same concept of using fonts to render images as [GUI Textures](../guitextures.md), but are 2 | a bit more difficult to implement for addon developers, as you need to create the font file yourself. 3 | 4 | Font files are stored under `assets/fonts/` and have [this format](https://minecraft.wiki/w/Resource_Pack#Fonts). 5 | You might also want to take a look at [our font for the jetpack energy bar overlay](https://github.com/Nova-Addons/Jetpacks/blob/main/src/main/resources/assets/fonts/energy_bar.json). 6 | 7 | ## ActionBarOverlay 8 | 9 | After creating your font, implement the `ActionbarOverlay` interface. There you'll need to provide the component 10 | to be displayed in the action bar. To improve performance, you can also override the `getWidth` function which should 11 | return the width of the overlay in pixels. Otherwise, this width will be calculated at runtime. 12 | 13 | ??? example "Example: JetpackOverlay" 14 | 15 | ```kotlin 16 | class JetpackOverlay : ActionbarOverlay { 17 | 18 | override var component: Component = getCurrentComponent() 19 | private set 20 | 21 | var percentage: Double = 0.0 22 | set(value) { 23 | require(value in 0.0..1.0) 24 | if (field == value) 25 | return 26 | 27 | field = value 28 | component = getCurrentComponent() 29 | } 30 | 31 | private fun getCurrentComponent(): Component { 32 | val stage = (percentage * 38).toInt() 33 | 34 | return Component.text() 35 | .move(95) // moves the cursor position to the right by 95 pixels 36 | .append(Component.text(('\uF000'.code + stage).toChar().toString()).font("jetpacks:energy_bar")) 37 | .build() 38 | } 39 | ``` 40 | 41 | The overlay can now be displayed through the `ActionbarOverlayManager`: 42 | 43 | ```kotlin 44 | ActionbarOverlayManager.registerOverlay(player, overlay) 45 | ``` 46 | 47 | !!! info 48 | 49 | Nova intercepts action bar packets and appends the action bar overlay to it. 50 | This means that normal action bar text can still be displayed, even if one or more action bar overlays are active. -------------------------------------------------------------------------------- /docs/nova/addon/fonts/bossbar.md: -------------------------------------------------------------------------------- 1 | Boss bar overlays are generally similar to action bar overlays, with the difference that they're rendered at boss bar 2 | position, can be moved vertically, and have a built-in way to dynamically position themselves above/below other overlays. 3 | 4 | ## Vertically Moved Fonts 5 | Boss bars in Minecraft generally have a fixed spacing of 19px. However, we need to be able to move overlays at 1px increments, 6 | which is why Nova gets rid of all vanilla boss bars and re-renders them using custom characters that have a boss bar texture. 7 | Additionally, we also create what we call "vertically moved fonts", which are variations of a font that are moved on the 8 | vertical axis. 9 | 10 | When creating a custom font for a boss bar overlay, you will also need to 11 | [create those vertically moved font variations.](fonts.md#vertical-movement) 12 | 13 | !!! info "Vertically moved fonts for `minecraft:default` will be automatically generated if the boss bar overlay is enabled in main config." 14 | 15 | After you've created your vertically moved fonts, you can start implementing the boss bar overlay in code. 16 | For that, you'll need to create a `BossBarOverlayCompound` consisting of at least one `BossBarOverlay`. 17 | 18 | ## BossBarOverlay 19 | Each `BossBarOverlay` defines a `Component` to be rendered at one specific vertical position. 20 | 21 | | property | description | 22 | |-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------| 23 | | offset | The vertical offset of this `BossBarOverlay` inside the `BossBarOverlayCompound`. | 24 | | centerX | The x coordinate at which the component should be centered at, with 0 being the middle of the screen. Can be null if no centering logic should be applied. | 25 | | component | The component to display. | 26 | 27 | Similar to action bar overlays, you can override the `getWidth` and also the `getVerticalRange` methods to improve performance. 28 | 29 | ## BossBarOverlayCompound 30 | A `BossBarOverlayCompound` is a collection of `BossBarOverlays`. The overlays in this compound will never be separated. 31 | 32 | | property | description | 33 | |-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 34 | | overlays | The list of `BossBarOverlays` that are part of the `BossBarOverlayCompound`. | 35 | | positioning | The `BarPositioning` that specifies at which position the `BossBarOverlayCompound` should be rendered. | 36 | | hasChanged | Whether any component of the included overlays have been changed and should be re-rendered. If you set this boolean to true, the `BossBarOverlayManager` will update your overlay and set the boolean to false again. | 37 | 38 | The `BossBarOverlayCompound` can then be registered: 39 | ```kotlin 40 | BossBarOverlayManager.registerOverlay(player, compound) 41 | BossBarOverlayManager.unregisterOverlay(player, compound) 42 | ``` 43 | 44 | ### BarPositioning 45 | The `BarPositioning` determines where your overlay should be rendered. You can choose between `BarPositioning.Fixed` and 46 | `BarPositioning.Dynamic`, where the latter will automatically move your overlay to prevent it from overlapping with other 47 | overlays or vanilla boss bars. Both types of positioning also allow you to define `BarMatchers`, which are used to determine 48 | whether your overlay should be placed above or below another overlay. 49 | `BarMatchers` can be both hard-coded or deserialized from a yaml configuration using `ConfigurationSection.getDeserialized(path)`. 50 | See [Configuration - WAILA Positioning](../../admin/configuration.md#waila-positioning) for a more detailed explanation. -------------------------------------------------------------------------------- /docs/nova/addon/fonts/guitextures.md: -------------------------------------------------------------------------------- 1 | ## Creating a GuiTexture 2 | 3 | Register your gui texture using initialization: 4 | 5 | ```kotlin 6 | @Init(stage = InitStage.PRE_PACK) // (1)! 7 | object GuiTextures { 8 | 9 | val EXAMPLE = ExampleAddon.guiTexture("example") { 10 | alignment(/*...*/) // (2)! 11 | path("gui/example") 12 | } 13 | 14 | } 15 | ``` 16 | 17 | 1. Nova will load this class during addon initialization, causing your gui textures to be registered. 18 | 2. The alignment of the texture. Depending on the inventory that your gui texture is intended for, and 19 | how the texture is intended to be displayed (left-aligned, centered, etc.) you can set the alignment accordingly. 20 | 21 | ## Using GuiTextures 22 | 23 | In order to use a gui texture, call the `getTitle` method on it, and use the resulting title for a Gui. 24 | If you're making a [tile-entity menu](../tile-entity/gui.md), the `GuiTexture` can be set directly in its constructor. -------------------------------------------------------------------------------- /docs/nova/addon/hitboxes.md: -------------------------------------------------------------------------------- 1 | ## Hitbox Implementations 2 | 3 | Nova provides you with two built-in ways to handle left- and right-click detection in game. 4 | 5 | ### PhysicalHitbox 6 | 7 | A `PhysicalHitbox` uses an [Interaction Entity](https://minecraft.wiki/w/Interaction). 8 | This means that the hitbox can be shown by pressing `F3+B` and functions similar to a normal entity, with it 9 | not allowing to interact with anything behind it. 10 | `PhysicalHitboxes` are also restricted to a square base area, as they only have a `width` and a `height`. 11 | 12 | ```kotlin 13 | // create the hitbox 14 | val hitbox = PhysicalHitbox(center, width, height) 15 | // add click handlers 16 | hitbox.addLeftClickHandler { /* ... */ } 17 | hitbox.addRightClickHandler { /* ... */ } 18 | // spawn the hitbox 19 | hitbox.register() 20 | // despawn the hitbox 21 | hitbox.unregister() 22 | ``` 23 | 24 | ### VirtualHitbox 25 | 26 | `VirtualHitboxes` use server-side ray casts, which allows for more complex and flexible hitboxes. 27 | For one, their size is completely customizable, and they're not limited to a square base area. 28 | Additionally, you can also specify a `qualifier` function, which allows you to filter out certain cases where 29 | the hitbox should not be triggered and the player should be able to interact with the block behind it. 30 | 31 | You can show all virtual hitboxes using the command `/nova debug showHitboxes`. 32 | 33 | ```kotlin 34 | // create the hitbox 35 | val hitbox = VirtualHitbox(from, to) 36 | // set the qualifier function 37 | hitbox.setQualifier { /* ... */ } 38 | // add click handlers 39 | hitbox.addLeftClickHandler { /* ... */ } 40 | hitbox.addRightClickHandler { /* ... */ } 41 | // spawn the hitbox 42 | hitbox.register() 43 | // despawn the hitbox 44 | hitbox.unregister() 45 | ``` 46 | 47 | ## Hit Location 48 | 49 | When a player clicks a hitbox, you might be supplied with a `Vector3f location` parameter. This location is relative 50 | to the center of the base area of the hitbox. To determine the block face that was clicked, you can use the 51 | `Hitbox.determineBlockFace(location: Vector3f)` function. 52 | 53 | !!! info 54 | 55 | Whether or not you're supplied with a `location` parameter depends on the click type and hitbox implementation. 56 | For physical hitboxes, only right-clicks will supply you with a location, while for virtual hitboxes, both left- 57 | and right-clicks will supply you with a location. -------------------------------------------------------------------------------- /docs/nova/addon/index.md: -------------------------------------------------------------------------------- 1 | **This guide is not beginner-friendly! Creating Nova addons requires advanced knowledge of Kotlin, the Paper API, and Gradle.** 2 | 3 | ## Setting up your project 4 | 5 | You can create a new repo using our addon template [here](https://github.com/xenondevs/Nova-Addon-Template/generate). 6 | After creating the repo and cloning it, make sure to edit the following files: 7 | 8 | ### src/main/kotlin 9 | 10 | Change the package name to your own. 11 | 12 | ### settings.gradle.kts 13 | 14 | Change `rootProject.name` to your addon id. 15 | 16 | ### build.gradle.kts 17 | 18 | Change `group` to your group. 19 | Change `version` to your version. 20 | 21 | In the `addon` task, set `main` to your addon main class. 22 | 23 | ## Adding dependencies 24 | 25 | If your addon requires dependencies that need to be present at runtime, add them under the `libraryLoader` configuration: 26 | 27 | ```kotlin title="build.gradle.kts dependencies { }" 28 | libraryLoader("commons-net:commons-net:3.8.0") 29 | ``` 30 | 31 | The requested library will be downloaded at startup. Note that using this mechanism exposes **all** urls of the 32 | maven repositories you use in your build configuration. 33 | 34 | ## Building 35 | 36 | To build, run 37 | ```bash title="Build with Gradle" 38 | gradlew addonJar -PoutDir="" 39 | ``` 40 | 41 | ## Enabling dev mode 42 | 43 | To enable dev mode, add the `NovaDev` argument using `-DNovaDev`. 44 | This allows you to bypass some restrictions like using addons that require a different version of Nova and 45 | enables general-purpose debugging functionality. 46 | 47 | Additionally, you can use `-DNovaForceRegenerateResourcePack` to force the resource pack to be regenerated on startup. 48 | 49 | ## KDoc 50 | 51 | The generated KDoc for Nova can be found on [here](https://nova.dokka.xenondevs.xyz/). -------------------------------------------------------------------------------- /docs/nova/addon/initialization.md: -------------------------------------------------------------------------------- 1 | # Initialization 2 | 3 | Simply annotate any singleton object with `#!kotlin @Init` and Nova will load the class during 4 | the specified initialization stage. 5 | 6 | ```kotlin title="Example initializable class" 7 | @Init(stage = InitStage.PRE_PACK) 8 | object Example 9 | ``` 10 | 11 | ## Dispatcher 12 | 13 | You can also define an initialization dispatcher: `SYNC` (default) to perform the initialization synchronously with all 14 | other initializables, or `ASYNC` to perform the initialization asynchronously, in parallel with other async initializables. 15 | 16 | ```kotlin 17 | @Init( 18 | stage = InitStage.POST_WORLD, 19 | dispatcher = Dispatcher.ASYNC 20 | ) 21 | object Example { 22 | 23 | @InitFun 24 | private fun init() { 25 | // ... 26 | } 27 | 28 | } 29 | ``` 30 | 31 | ## Initialization Dependencies 32 | 33 | If the pre-defined initialization stages are not enough for you, you can also configure which classes that should be 34 | initialized before (`runAfter`) or after (`runBefore`) your class: 35 | 36 | ```kotlin title="Example initializable class with dependencies" 37 | @Init( 38 | stage = InitStage.PRE_PACK, 39 | runAfter = [ClassA::class], // This class will be initialized after ClassA 40 | runBefore = [ClassB::class] // This class will be initialized before ClassB 41 | ) 42 | object Example 43 | ``` 44 | 45 | ## Functions 46 | 47 | If your class is annotated with `#!kotlin @Init`, you can also annotate your functions with `#!kotlin @InitFun` 48 | and `#!kotlin @DisableFun`. There, you can also configure initialization dependencies and a dispatcher. 49 | For `#!kotlin @InitFun`. 50 | 51 | * `#!kotlin @InitFun`: Specify one or more functions that should be called during initialization. 52 | * `#!kotlin @DisableFun` Specify one or more functions that should be called when your addon is disabled. 53 | 54 | ```kotlin title="Example initializable class with functions" 55 | @Init(stage = InitStage.PRE_PACK) 56 | object Example { 57 | 58 | @InitFun 59 | private fun init() { 60 | //Run during PRE_PACK init stage 61 | } 62 | 63 | @DisableFun 64 | private fun disable() { 65 | // Run when the addon is disabled 66 | } 67 | 68 | } 69 | ``` -------------------------------------------------------------------------------- /docs/nova/addon/items/enchantments.md: -------------------------------------------------------------------------------- 1 | # Enchantments 2 | 3 | ## Enchantable Items 4 | 5 | To make a `NovaItem` enchantable, add the `Enchantable` behavior: 6 | 7 | ```kotlin 8 | val EXAMPLE_ITEM = registerItem("example_item", Enchantable()) 9 | ``` 10 | 11 | ```yaml title="configs/example_item.yml" 12 | enchantment_value: 10 # (1)! 13 | supported_enchantments: # (2)! 14 | - minecraft:sharpness 15 | - minecraft:smite 16 | - minecraft:bane_of_arthropods 17 | - minecraft:knockback 18 | - minecraft:fire_aspect 19 | - minecraft:looting 20 | - minecraft:sweeping 21 | - minecraft:unbreaking 22 | - minecraft:mending 23 | ``` 24 | 25 | 1. The enchantment value of the item. This value defines how enchantable an item is. 26 | A higher enchantment value means more secondary and higher-level enchantments. 27 | Vanilla enchantment values: wood: `15`, stone: `5`, iron: `14`, diamond: `10`, gold: `22`, netherite: `15` 28 | 2. The supported enchantments of this item. Since `primary_enchantments` is not defined, these are also the primary 29 | enchantments of the item. 30 | 31 | !!! abstract "`supported_enchantments` vs. `primary_enchantments`" 32 | 33 | Supported enchantments: The enchantments that can be applied to the item, i.e. via anvil or commands. 34 | Primary enchantments: The enchantments that appear in the enchanting table. 35 | 36 | If you're defining `supported_enchantments` via the config, `primary_enchantments` will default to `supported_enchantments`. 37 | Once you add `primary_enchantments` to the config, you'll need to add all enchantments from `supported_enchantments` 38 | to `primary_enchantments` it. 39 | 40 | ### Custom Enchantments 41 | 42 | You can register a custom enchantment like this: 43 | 44 | ```kotlin title="Enchantments.kt" 45 | @Init(stage = InitStage.PRE_PACK) // (1)! 46 | object Enchantments { 47 | 48 | val EXAMPLE_ENCHANTMENT = ExampleAddon.enchantment("example_enchantment") { 49 | // ... 50 | } 51 | 52 | } 53 | ``` 54 | 55 | !!! abstract "Refer to the [KDocs](https://nova.dokka.xenondevs.xyz/nova/xyz.xenondevs.nova.world.item.enchantment/-enchantment-builder/index.html) for list of available functions and properties." -------------------------------------------------------------------------------- /docs/nova/addon/items/equipment.md: -------------------------------------------------------------------------------- 1 | ## Creating equipment 2 | 3 | Custom equipment (armor) can be registered like this: 4 | 5 | ```kotlin 6 | @Init(stage = InitStage.PRE_PACK) // (1)! 7 | object Equipment { 8 | 9 | val EXAMPLE = ExampleAddon.equipment("example") { 10 | humanoid { // (2)! 11 | layer { // (3)! 12 | texture("example") // (4)! 13 | emissivityMap("example_emissivity_map") // (5)! 14 | dyeable(Color.WHITE) // (8)! 15 | } 16 | } 17 | 18 | humanoidLeggings { 19 | layer { 20 | texture("example") // (6)! 21 | emissivityMap("example_emissivity_map") // (7)! 22 | } 23 | } 24 | } 25 | 26 | } 27 | ``` 28 | 29 | 1. Nova will load this class during addon initialization, causing your armor to be registered. 30 | 2. The type of equipment. You can also create equipment for horses, dogs, and more. 31 | 3. You can layer multiple textures on top of each other. This layering is flat, there is no three-dimensional effect 32 | like for the player skin. 33 | 4. The path to the texture. This resolves the file under `textures/entity/equipment/humanoid/example.png` 34 | 5. (optional) The path to the emissivity map. This resolves the file under `textures/entity/equipment/humanoid/example_emissivity_map.png` 35 | Black pixels are interpreted as not emissive, white pixels are interpreted as fully emissive. 36 | 6. The path to the texture. This resolves the file under `textures/entity/equipment/humanoid/example.png` 37 | 7. (optional) The path to the emissivity map. This resolves the file under `textures/entity/equipment/humanoid/example_emissivity_map.png` 38 | Black pixels are interpreted as not emissive, white pixels are interpreted as fully emissive. 39 | 8. (optional) Makes this layer dyeable and uses `Color.WHITE` if no dye is applied. 40 | Dyeable items also require the `Dyeable` item behavior. 41 | 42 | After creating the armor, you can apply it to items using the `Equippable` behavior: 43 | 44 | ```kotlin 45 | val EXAMPLE_HELMET = registerItem("example_helmet", Equippable(Armor.EXAMPLE, EquipmentSlot.HEAD)) 46 | ``` 47 | 48 | ## Animated equipment 49 | 50 | You can also create animated armor: 51 | 52 | ```kotlin title="Equipment.kt" 53 | val EXAMPLE = ExampleAddon.animatedEquipment("example") { 54 | humanoid { 55 | layer { 56 | texture(5, InterpolationMode.NONE, "frame_1", "frame_2", "frame_3") // (1)! 57 | } 58 | } 59 | 60 | /* ... */ 61 | } 62 | ``` 63 | 64 | 1. Creates a non-interpolated animation that switches through the three defined frames every 5 ticks. 65 | Note that interpolation is achieved by pre-generating all interpolated frames, which may drastically 66 | increase resource pack size. 67 | 68 | ## Compatibility with client-side rendering mods 69 | 70 | Emissive textures are implemented via a core shader and are not compatible with shader mods. -------------------------------------------------------------------------------- /docs/nova/addon/items/item-behaviors.md: -------------------------------------------------------------------------------- 1 | # Item Behaviors 2 | 3 | In Nova, item logic is implemented via `ItemBehaviors`. 4 | There are some default behaviors available, but you can also create your own custom behaviors. 5 | 6 | ## Default Item Behaviors 7 | 8 | For a list of all available default item behaviors, refer to the [KDocs](https://nova.dokka.xenondevs.xyz/nova/xyz.xenondevs.nova.world.item.behavior/index.html). 9 | 10 | ## Custom Item Behaviors 11 | 12 | You can create a custom item behavior by implementing the `ItemBehavior` interface. 13 | 14 | There, you'll be able to override `baseDataComponents`, which are the default 15 | [data components](https://minecraft.wiki/w/Data_component_format) of `NovaItems` with that behavior. 16 | 17 | ### Vanilla material properties 18 | 19 | Some functionality can not yet be achieved by using data components, as it is still bound to the vanilla item type. 20 | For such cases, you can specify [VanillaMaterialProperties](https://nova.dokka.xenondevs.xyz/nova/xyz.xenondevs.nova.world.item.vanilla/-vanilla-material-property/index.html) 21 | which will change the **client-side** item type. 22 | 23 | ### Client-side item stack 24 | 25 | To modify the [client-side item](using-nova-item.md#client-side-items), you can override `modifyClientSideStack`. 26 | The data of the client-side stack will not be stored in the world and is only intended for display purposes. 27 | Furthermore, the components of the client-side stack will not affect the tooltip, e.g. adding the `DAMAGE` component 28 | will not cause the damage value to be shown in the advanced tooltip. (Assuming advanced tooltips are handled by Nova 29 | via `/nova advancedTooltips`). 30 | 31 | ### ItemBehaviorHolder and ItemBehaviorFactory 32 | 33 | `ItemBehaviorHolder` is a sealed interface with two implementations: `ItemBehavior` and `ItemBehaviorFactory`, where 34 | `ItemBehaviorFactory` creates `ItemBehavior` instances based on a `NovaItem` instance. This allows you to create 35 | factories for your `ItemBehaviors` that read from the item's config file. 36 | 37 | ```kotlin title="Example custom ItemBehavior with ItemBehaviorFactory" 38 | class MyBehavior(value: Provider) : ItemBehavior { 39 | 40 | private val value by value // (1)! 41 | 42 | companion object : ItemBehaviorFactory { 43 | 44 | override fun create(item: NovaItem): MyBehavior { 45 | return MyBehavior(item.config.entry("value")) 46 | } 47 | 48 | } 49 | 50 | } 51 | ``` 52 | 53 | 1. Delegating to the obtained provider makes this property config-reloadable without any additional code. 54 | 55 | Now, you could, for example, assign the same `ItemBehaviorFactory` to multiple items, while still accessing 56 | different configs. 57 | 58 | ```kotlin 59 | @Init(stage = InitStage.PRE_PACK) // (1)! 60 | object Items { 61 | 62 | val EXAMPLE_ITEM_1 = ExampleAddon.registerItem("example_1", MyBehavior) // configs/example_1.yml 63 | val EXAMPLE_ITEM_2 = ExampleAddon.registerItem("example_2", MyBehavior) // configs/example_2.yml 64 | val EXAMPLE_ITEM_3 = ExampleAddon.registerItem("example_3", MyBehavior) // configs/example_3.yml 65 | 66 | } 67 | ``` 68 | 69 | 1. Nova will load this class during addon initialization, causing the item fields to be initialized and your items to be registered. 70 | 71 | ## Item Data 72 | 73 | Data for Nova's ItemStacks can be stored in a `NamespacedCompound`, which serializes data using [CBF](../../../../cbf/). 74 | You can retrieve the `NamespacedCompound` of an `ItemStack` by calling `#!kotlin ItemStack.novaCompound`. 75 | After updating it, you'll need to write it back to the `ItemStack`. 76 | 77 | Alternatively, you can also read and write data using `#!kotlin ItemStack.storeData` and `#!kotlin ItemStack.retrieveData`, 78 | which access the `NamespacedCompound` for you. 79 | 80 | `NovaItems` can also have default data stored in their `NamespacedCompound`, which can be applied using the `defaultCompound` 81 | property in a custom `ItemBehavior`. 82 | 83 | Of course, you can also use Bukkit's [persistent data container](https://docs.papermc.io/paper/dev/pdc) for data storage. -------------------------------------------------------------------------------- /docs/nova/addon/migration-guide/0.11-0.12.md: -------------------------------------------------------------------------------- 1 | # Migration Guide 0.11 ➝ 0.12 2 | 3 | ## Building 4 | 5 | You will now need to set the kotlin compiler jvm target to `17`: 6 | ```kotlin title="build.gradle.kts" 7 | tasks { 8 | withType { 9 | kotlinOptions { 10 | jvmTarget = "17" 11 | } 12 | } 13 | } 14 | ``` 15 | 16 | ## BlockOptions 17 | 18 | The `BlockOptions` constructor has changed. You will now need to set a list of `ToolCategories` and a `SoundGroup` 19 | instead of individual sounds. Refer to [BlockOptions](../blocks/creating-blocks#blockoptions) 20 | for more information. 21 | 22 | ## ItemBehavior 23 | 24 | All configurable `ItemBehaviors` have been reworked to load their data from configs using the new `MaterialOptions`. 25 | In most cases, you'll no longer need to instantiate them, but rather just use their companion object in the `NovaItem` 26 | constructor and configure them in the material-specific config file. Those properties can now be accessed in code using `.options.` 27 | instead of `.`. Refer to [ItemBehaviors](../items/item-behaviors.md) for more information. 28 | 29 | The `getLore` and `getName` methods have been replaced by `updatePacketItemData` which combines those two methods and 30 | adds more options. 31 | 32 | ## Overlays 33 | 34 | The `width` (BossBarOverlay, ActionBarOverlay) and `endY` (BossBarOverlay) were removed. You can still set them by 35 | overwriting the `getWidth` and `getEndY` methods, but this is no longer mandatory. 36 | 37 | ## Misc 38 | 39 | - Some NovaMaterialRegistry functions have been slightly changed to include the new `maxStackSize` parameter 40 | - The `ToolUtils.damgeTool` functions have been moved to `DamageableUtils.damageItem` 41 | - `SoundEffect` has been removed 42 | - `ValueReloadable` has been replaced by `Provider`. 43 | - General refactoring: You might need to change some imports regarding item (behaviors), tools, etc. -------------------------------------------------------------------------------- /docs/nova/addon/migration-guide/0.13-0.14.md: -------------------------------------------------------------------------------- 1 | # Migration Guide 0.13 ➝ 0.14 2 | 3 | This release is mainly just the update to 1.20, but there are a few changes that you should be aware of. 4 | 5 | ## Removed `spigotResourceId` from the addon description and the related gradle task 6 | 7 | Instead, you can now set a list of `ProjectDistributors` in code, which will be used to check for updates: 8 | 9 | ```kotlin title="MyAddon.kt" 10 | object MyAddon : Addon() { 11 | 12 | override val projectDistributors = listOf( 13 | ProjectDistributor.hangar(/*project id*/), //(1)! 14 | ProjectDistributor.modrinth(/*project id*/), //(2)! 15 | ProjectDistributor.github(/*project id*/) //(3)! 16 | ) 17 | 18 | } 19 | ``` 20 | 21 | 1. Example: 22 | `#!kotlin ProjectDistributor.hangar("xenondevs/Machines")` 23 | 2. Example: 24 | `#!kotlin ProjectDistributor.modrinth("nova-framework")` 25 | 3. Example: 26 | `#!kotlin ProjectDistributor.github("Nova-Addons/Machines")` 27 | 28 | ## Changed `NovaRecipe.key: NamspacedKey` to `NovaRecipe.id: ResourceLocation` 29 | 30 | In the effort to convert everything from `NamespacedId` / `NamespacedKey` to `ResourceLocation`, the `NovaRecipe` 31 | class has now also been updated to use `ResourceLocation`. If you've created a custom recipe deserializer, you'll probably 32 | now also need to switch to `RecipeDeserializer#getRecipeId(File)`. 33 | 34 | ## Smithing Recipes 35 | 36 | Since Mojang has changed smithing, the `SMITHING_TRANSFORM` and recipe files now also need to be in a `smithing_transform` 37 | directory. You'll also now need to specify a `template` ingredient. 38 | There is currently no support for smithing trim recipes. -------------------------------------------------------------------------------- /docs/nova/addon/migration-guide/0.14-0.15.md: -------------------------------------------------------------------------------- 1 | ## Paper migration 2 | 3 | Nova has migrated to Paper. This includes significant changes to the build environment, so check out the 4 | [Nova-Addon-Template](https://github.com/xenondevs/Nova-Addon-Template) on GitHub for an up-to-date example. 5 | This also means that you'll no longer need to run BuildTools. 6 | It's also no longer possible to reload the server, even if you're in dev mode with `-DNovaDev`, but this didn't work 7 | properly before anyway. 8 | 9 | ## Init annotation 10 | 11 | The `#!kotlin @Init` annotation now requires a `stage` parameter. [More info](../misc/initialization.md#initialization-stages) 12 | 13 | ## Configs 14 | 15 | Nova now uses a fork of [SpongePowered/Configurate](https://github.com/SpongePowered/Configurate), `configReloadable` 16 | has been deprecated and nova items- and blocks now properly have a config associated with them. 17 | 18 | ```kotlin title="old" 19 | val A: Long by configReloadble { NovaConfig[Items.EXAMPLE_ITEM].getLong("some.value") } 20 | val B: Long by configReloadable { NovaConfig["namespace:name"].getLong("some.value") } 21 | ``` 22 | 23 | ```kotlin title="new" 24 | val A: Long by Items.EXAMPLE_ITEM.config.entry("some", "value") 25 | val B: Long by Configs["namespace:name"].entry("some", "value") 26 | ``` 27 | 28 | [More Info](../configs.md) 29 | 30 | ## ItemBehavior 31 | 32 | * `ItemBehavior` is now an interface. 33 | * The same `ItemBehavior` instance can now be used for multiple `NovaItem`s. 34 | * The `modifyItemBuilder` function has been deprecated in favor of a new `getDefaultCompound` function. 35 | * Many default item behaviors such as `Damageable` are now also interfaces. 36 | This allows you to (for example) implement your own damaging logic (such as using energy). 37 | * All item behavior `*Options` classes (such as `FoodOptions`, `ToolOptions`, etc.) have been removed as 38 | the properties have been moved to the new item behavior interfaces. 39 | * `NovaItem#getBehavior` now asserts non-null by default. Added `NovaItem#getBehaviorOrNull` for nullable access. 40 | * `ItemBehavior#handleInteract` now accepts `WrappedPlayerInteractEvent` instead of `PlayerInteractEvent`. This wrapping 41 | class has an additional parameter called `actionPerformed`, which is used to signal whether a custom action has been run. 42 | Additionally, `handleInteract` is now always called, even if an action (such as a tile-entity gui being opened) has 43 | already been performed. 44 | 45 | [More Info](../items/item-behaviors.md) 46 | 47 | ### Utility functions 48 | 49 | ItemBehavior-related utility function such as those in `DamageableUtils` have been moved to the companion object of 50 | their respective `ItemBehavior`. 51 | 52 | ## Packet Event Listeners 53 | 54 | Packet Event listeners now need a `PacketListener` interface. [More Info](../misc/events.md#working-with-packet-events) 55 | 56 | ## Advancements 57 | 58 | The advancement dsl builder has been removed. You can still register advancements via `AdvancementLoader`, but you'll 59 | need to use Mojang's internal advancement builders. The built-in utility functions for creating advancements about 60 | obtaining Nova items (`obtainNovaItemAdvancement`, `obtainNovaItemsAdvancement`) are still available. -------------------------------------------------------------------------------- /docs/nova/addon/migration-guide/0.15-0.16.md: -------------------------------------------------------------------------------- 1 | ## Minecraft version change 2 | 3 | Updated to Minecraft 1.20.4 -------------------------------------------------------------------------------- /docs/nova/addon/migration-guide/0.18-0.19.md: -------------------------------------------------------------------------------- 1 | ## Minecraft version 2 | 3 | Updated to 1.21.5 4 | 5 | ## InvUI 6 | 7 | Updated InvUI to 2.0.0-alpha.8, which contains numerous breaking changes. 8 | See the [InvUI GitHub release page](https://github.com/NichtStudioCode/InvUI/releases/tag/2.0.0-alpha.8) 9 | for details. 10 | 11 | ## ItemBehavior 12 | 13 | - `#!kotlin ItemBehavior.defaultPatch` has been removed with no replacement 14 | - `#!kotlin ItemBehavior.baseDataComponents` is now a provider of `xyz.xenondevs.nova.world.item.DataComponentMap` 15 | instead of the nms `DataComponentMap`. The new `DataComponentMap` allows the usage of Paper's data component types. 16 | Use the top-level function `buildDataComponentMapProvider { }` for a builder DSL. 17 | - `#!kotlin ItemBehavior#handleEquip` has been updated to use Paper's `EntityEquipmentChangedEvent`. 18 | Note that this handler is fired more often than the old one, such as when joining the server or when equipping 19 | an item in a hand. Nova's custom `ArmorEquipEvent` has been removed. 20 | - Removed overloads for default item behavior factory functions (and updated their constructors) that 21 | accepted `Holder`. Use `Key` instead. 22 | 23 | ## Addon Registries 24 | 25 | The registry interfaces (such as `ItemRegistry`, `BlockRegistry`) have been deprecated. 26 | All registration functions are now available on the `Addon` object. 27 | 28 | === "0.18" 29 | ```kotlin 30 | @Init(stage = InitStage.PRE_PACK) 31 | object Equipment : EquipmentRegistry by Machines.registry { 32 | 33 | val STAR = equipment("star") { 34 | humanoid { 35 | layer { 36 | texture("star") 37 | } 38 | } 39 | humanoidLeggings { 40 | layer { 41 | texture("star") 42 | } 43 | } 44 | } 45 | 46 | } 47 | ``` 48 | 49 | === "0.19" 50 | ```kotlin 51 | import xyz.xenondevs.nova.addon.machines.Machines.equipment 52 | 53 | @Init(stage = InitStage.PRE_PACK) 54 | object Equipment { 55 | 56 | val STAR = equipment("star") { 57 | humanoid { 58 | layer { 59 | texture("star") 60 | } 61 | } 62 | humanoidLeggings { 63 | layer { 64 | texture("star") 65 | } 66 | } 67 | } 68 | 69 | } 70 | ``` -------------------------------------------------------------------------------- /docs/nova/addon/misc/events.md: -------------------------------------------------------------------------------- 1 | # Events 2 | 3 | ## Working with Bukkit Events 4 | 5 | Of course, you can also use Bukkit's events. To register an event listener, use the `#!kotlin Listener.registerEvents()` 6 | extension function. 7 | 8 | ## Working with Packet Events 9 | 10 | You can also listen to incoming and outgoing packets. 11 | To do so, implement the `PacketListener` interface and register your listener using the 12 | `#!kotlin PacketListener.registerPacketHandler()` extension function. 13 | Then, you can use the `#!kotlin @PacketHandler` annotation to mark event methods. 14 | 15 | !!! abstract "Packet Event Types" 16 | 17 | Please note that this currently does not include all possible packets, as the system is still in development. 18 | Feel free to open an issue or a pull request if you need another packet type. 19 | 20 | ## Calling events from Nova's Plugin API 21 | 22 | You might've noticed that the `nova-api` module is not in your classpath. This module is explicitly for developers of 23 | third party plugins and provides a stable API for Nova. To prevent addon developers from accidentally using those 24 | classes, `nova-api` is not a transitive dependency of `nova`. 25 | 26 | To still call events from Nova's Plugin API, use the `NovaEventFactory`: 27 | 28 | ```kotlin 29 | // obtain drops from the block 30 | val drops: MutableList = block.getAllDrops().toMutableList() 31 | // call the event (might mutate drops list) 32 | NovaEventFactory.callTileEntityBlockBreakEvent(this, block, drops) 33 | ``` 34 | -------------------------------------------------------------------------------- /docs/nova/addon/misc/project-distributors.md: -------------------------------------------------------------------------------- 1 | # Configuring project distributors for update notifications 2 | 3 | To set up update notifications, simply override the `projectDistributors` list in your addon object: 4 | 5 | ```kotlin title="MyAddon.kt" 6 | object MyAddon : Addon() { 7 | 8 | override val projectDistributors = listOf(/*your distributors*/) 9 | 10 | } 11 | ``` 12 | 13 | By default, there are three different distributors available: 14 | 15 | | Distributor | Code | 16 | |-------------|-----------------------------------------------| 17 | | Hangar | `ProjectDistributor.hangar(/*project id*/)` | 18 | | Modrinth | `ProjectDistributor.modrinth(/*project id*/)` | 19 | | GitHub | `ProjectDistributor.github(/*project id*/)` | 20 | 21 | You can also create your own distributor by implementing the `ProjectDistributor` interface. 22 | 23 | !!! abstract "Order of update checks" 24 | 25 | When checking for updates, all registered distributors are checked in the order they are specified in the list. 26 | This means that if you want users to download your updates from a specific distributor, you should put it at the 27 | top of the list. 28 | 29 | !!! abstract "Pre-release versions" 30 | 31 | Users will only be notified of pre-release versions if they themselves are using a pre-release version. -------------------------------------------------------------------------------- /docs/nova/addon/misc/scheduler.md: -------------------------------------------------------------------------------- 1 | To access the Bukkit scheduler, you can use the top-level functions `runTask`, `runTaskLater`, `runTaskTimer`, 2 | `runAsyncTask`, `runAsyncTaskLater`, and `runAsyncTaskTimer`. -------------------------------------------------------------------------------- /docs/nova/addon/resourcepack/addon-assets.md: -------------------------------------------------------------------------------- 1 | !!! tip "Minecraft Resource Pack Format" 2 | 3 | Before continuing here, make sure to familiarize yourself with the 4 | [Minecraft Resource Pack Format](https://minecraft.wiki/w/Resource_pack#Pack_format) if you haven't already. 5 | 6 | All assets intended for the resource pack should be placed in `src/main/resources/assets/`. 7 | Nova will automatically read and extract assets from the following subdirectories into the final resource pack: 8 | 9 | - `atlases/` - [Wiki](https://minecraft.wiki/w/Resource_pack#Atlases) 10 | - `textures/` - [Wiki](https://minecraft.wiki/w/Resource_pack#Textures) 11 | - `models/` - [Wiki](https://minecraft.wiki/w/Resource_pack#Models) (unused models will not be included) 12 | - `lang/` - [Wiki](https://minecraft.wiki/w/Resource_pack#Language) 13 | - `fonts/` - [Wiki](https://minecraft.wiki/w/Resource_pack#Fonts) 14 | - `sounds/` and `sounds.json` - [Wiki](https://minecraft.wiki/w/Resource_pack#Sounds) 15 | 16 | ## Models 17 | 18 | For creating custom block models, we recommend using [Blockbench](https://blockbench.net/). 19 | For [entity-backed blocks](../blocks/creating-blocks.md#model-backing), Nova allows you to create oversized models. 20 | If you want to take advantage of this, you'll need to deactivate Blockbench's size limit: 21 | 22 | ![](https://i.imgur.com/cbjOKZr.png){width=50%} 23 | 24 | ## Language Files 25 | 26 | Language files belong in the `lang` folder. 27 | The format for these files is the same one minecraft uses. 28 | If you need the locale code for a language, you can search for it [here](https://minecraft.wiki/w/Language). 29 | 30 | !!! warning 31 | 32 | **Make sure to use the "in-game" locale code since ISO-639-3 isn't implemented by Minecraft (yet?)** 33 | 34 | For English (United States), create a file called ``en_us.json`` in the ``lang`` folder. The format ``[]..`` 35 | should be used for the translations keys. The type can be left out if it's a generic message. In general, the following 36 | types should be used: 37 | 38 | * ``item`` - For translations related to items 39 | * ``block`` - For translations related to blocks 40 | * ``menu`` - For GUI related translations 41 | * ``inventory`` - For inventory names 42 | * ``container`` - For fluid container names 43 | * ``command`` - For command responses 44 | * ``advancement`` - For advancements 45 | 46 | You can of course use your own type names, just make sure to include your addons namespace in the key. 47 | 48 | ```json 49 | { 50 | "item.example_addon.ruby": "Ruby", 51 | "block.example_addon.solar_panel": "Solar Panel", 52 | "advancement.example_addon.ruby.title": "Ruby", 53 | "advancement.example_addon.ruby.description": "Acquire a ruby", 54 | "advancement.example_addon.solar_panel.title": "Clean Energy", 55 | "advancement.example_addon.solar_panel.description": "Craft a Solar Panel" 56 | } 57 | ``` -------------------------------------------------------------------------------- /docs/nova/addon/tile-entity/gui.md: -------------------------------------------------------------------------------- 1 | # TileEntity Menu 2 | 3 | !!! info "Check out the InvUI wiki" 4 | 5 | If you're not familiar with the InvUI library, you'll have trouble understanding the following guide. You can check 6 | out the wiki [here](../../../../../invui). Nova registers some default ingredients which can be used 7 | in the Gui Builder. You can check out the default ingredients [here](https://github.com/xenondevs/Nova/blob/main/nova/src/main/kotlin/xyz/xenondevs/nova/ui/GlobalStructureIngredients.kt). 8 | **Don't register your own global ingredients!** 9 | 10 | ## Creating a TileEntityMenu 11 | 12 | If you want your TileEntity to have a GUI, you'll have to create a TileEntityMenu class. You can do this by inheriting 13 | from either `GlobalTileEntityMenu` or `IndividiualTileEntityMenu`. The difference between the two is that the 14 | `GlobalTileEntityMenu` will only have one instance per tile-entity, while the `IndividualTileEntityMenu` will have 15 | one instance per player. 16 | 17 | To mark your menu class as a TileEntity menu, you'll have to annotate it with the `@TileEntityMenuClass` annotation. 18 | Nova will automatically instantiate your menu class when needed. 19 | (Note that your `NovaBlock` will require the `TileEntityInteractive` behavior to open the GUI.) 20 | 21 | !!! example "Example GUIs" 22 | 23 | === "GlobalTileEntityMenu" 24 | ```kotlin 25 | @TileEntityMenuClass 26 | private inner class SolarPanelMenu : GlobalTileEntityMenu() { 27 | 28 | override val gui = Gui.builder() 29 | .setStructure( 30 | "1 - - - - - - - 2", 31 | "| u # # e # # # |", 32 | "| # # # e # # # |", 33 | "| # # # e # # # |", 34 | "3 - - - - - - - 4") 35 | .addIngredient('u', OpenUpgradesItem(upgradeHolder)) 36 | .addIngredient('e', EnergyBar(3, energyHolder)) // (1)! 37 | .build() 38 | 39 | } 40 | ``` 41 | 42 | 1. The ``energyHolder`` will be explained in an upcoming section. 43 | 44 | For most cases, this is the menu class you'll want to extend. 45 | 46 | === "IndividualTileEntityMenu" 47 | ```kotlin 48 | @TileEntityMenuClass 49 | private inner class VacuumChestMenu(player: Player) : IndividualTileEntityMenu(player) { 50 | 51 | override val gui = Gui.builder() 52 | .setStructure( 53 | "1 - - - - - - - 2", 54 | "| s u # i i i p |", 55 | "| r # # i i i d |", 56 | "| f # # i i i m |", 57 | "3 - - - - - - - 4") 58 | /* ... */ 59 | .addIngredient('r', region.createVisualizeRegionItem(player)) // (1)! 60 | /* ... */ 61 | .build() 62 | 63 | } 64 | ``` 65 | 66 | 1. This is what we need the Player instance for. The visualize region button can be toggled by every player individually, 67 | which is why we need different `Gui` instances for each player. 68 |
69 | If you're intrested in the `region` field, you can check out the [Region](./region.md) section. 70 | 71 | !!! info "Gui Textures" 72 | 73 | If you want to use a [GuiTexture](../fonts/guitextures.md), simply pass it to the `GlobalTileEntityMenu` / `IndividualTileEntityMenu` constructor. -------------------------------------------------------------------------------- /docs/nova/addon/tile-entity/introduction.md: -------------------------------------------------------------------------------- 1 | Tile-Entities are blocks that have additional data and logic attached to them. 2 | 3 | ## Creating a Tile-Entity 4 | 5 | Before registering the `NovaBlock` for a tile-entity, you'll need to create a class that extends `TileEntity`: 6 | 7 | ```kotlin 8 | class ExampleTileEntity( 9 | pos: BlockPos, 10 | blockState: NovaBlockState, 11 | data: Compound 12 | ) : TileEntity(pos, blockState, data) { 13 | 14 | // ... 15 | 16 | } 17 | ``` 18 | 19 | Now, you can register the block: 20 | 21 | ```kotlin 22 | @Init(stage = InitStage.PRE_PACK) // (1)! 23 | object Blocks { 24 | 25 | val EXAMPLE_TILE_ENTITY = ExampleAddon.tileEntity("example_tile_entity", ::ExampleTileEntity) { 26 | behaviors( 27 | TileEntityLimited, // (2)! 28 | TileEntityDrops, // (3)! 29 | TileEntityInteractive // (4)! 30 | ) 31 | tickrate(20) // (5)! 32 | } 33 | 34 | } 35 | ``` 36 | 37 | 1. Nova will load this class during addon initialization, causing your blocks to be registered. 38 | 2. Enables [tile-entity limits](../../admin/configuration.md#tile-entity-limits). 39 | (Note that if this behavior is not present, tile-entity limit tracking will also be disabled and placing this tile-entity 40 | will not count towards global limits.) 41 | 3. Delegates the drop logic to `#!kotlin TileEntity.getDrops`. 42 | 4. Delegates interactions to `#!kotlin TileEntity.handleRightClick`. This is also required if your tile-entity has a [GUI](gui.md). 43 | 5. The rate at which `#!kotlin TileEntity.handleTick` function is called. 44 | This defaults to `20`, so you wouldn't need to specify it in this case. 45 | 46 | !!! danger "Tile-Entities are instantiated off-main" 47 | 48 | Tile-Entities are constructed off-main, so you cannot interact with world state during object construction. 49 | Also, it is not guaranteed that the chunk the tile-entity is in is loaded at the time of construction. 50 | For such cases, use `#!kotlin TileEntity.handleEnable()` and `#!kotlin TileEntity.handleDisable()`. 51 | 52 | Additionally, especially concerning tile-entites that are in spawn chunks, Nova might not be fully initialized 53 | when the tile-entity is constructed. As such, you cannot rely on anything that is not initialized pre world to be 54 | available during tile-entity construction. (Such as RecipeManager for example, which needs to wait for the initialization 55 | of supported third-party custom item plugins.) 56 | 57 | ## Accessing Tile-Entity data 58 | 59 | Tile-Entity data is stored in our CBF Format, so make sure to check out the [CBF Documentation](../../../../cbf). 60 | 61 | ??? example "Default Nova Binary Adapters" 62 | 63 | Nova provides binary adapters for the following types by default: 64 | `NamespacedCompound`, `Color`, `Location`, `NamespacedKey`, `NamespacedId`, `ResourceLocation`, `VirtualInventory`, 65 | `BlockPos`, `ItemStack`, `NetworkType`, `AbilityType`, `AttachmentType`, `RecipeType`, `Table`, `ItemFilter`, 66 | `NovaBlock`, `NovaItem`, `ToolCategory`, `ToolTier` 67 | 68 | You can register binary adapters for your own types as explained in the CBF Documentation. 69 | If you require a binary adapter for a type from Java / Minecraft / Paper, please request it on GitHub. 70 | 71 | To access tile-entity data, you can use the `storedValue` function to retrieve a `#!kotlin MutableProvider` to which you can delegate: 72 | 73 | ```kotlin title="storedValue (not null)" 74 | private val someInt: Int by storedValue("someInt") { 0 } 75 | ``` 76 | 77 | ```kotlin title="storedValue (nullable)" 78 | private val someString: String? by storedValue("someString") 79 | ``` 80 | 81 | And that's it! When the tile-entity gets saved, those properties will automatically be saved with it. 82 | 83 | !!! abstract "Providers are lazy" 84 | 85 | Since Providers are lazy, it is safe to use them to access things that are initialized post world, as long as 86 | their values are not resolved immediately. 87 | 88 | Alternatively, you can also use `retrieveData` and `storeData` functions to manually read and write data. 89 | For that, you may also want to override the `saveData` function. 90 | 91 | ### Utility functions for commonly stored data types 92 | 93 | There are a few utility functions available in `TileEntity` for storing commonly used data: 94 | 95 | * `storedInventory` - Creates or reads a [VirtualInventory](../../../../invui/inventory/#virtual-inventory) 96 | from internal storage. Also registers a drop provider that drops the inventory's contents when the tile-entity is 97 | destroyed. 98 | * `storedFluidContainer` - Creates or reads a `FluidContainer` from internal storage. 99 | * `storedRegion` - Creates or reads a [DynamicRegion](region.md#dynamic-region) from internal storage. -------------------------------------------------------------------------------- /docs/nova/addon/tile-entity/region.md: -------------------------------------------------------------------------------- 1 | ## Region 2 | 3 | Generally, a region is an area between a `min` and a `max` Location. 4 | 5 | ```kotlin 6 | val region = Region(min, max) 7 | ``` 8 | 9 | The `Region` companion object provides several utility functions for creating regions, such as: 10 | 11 | - `#!kotlin Region.surrounding(pos, radius)` 12 | - `#!kotlin Region.inFrontOf(tileEntity, ...)` 13 | - and more 14 | 15 | ### Visualization 16 | 17 | To display the outline of this region with particles, you can use the `VisualRegion` object: 18 | 19 | ```kotlin 20 | // to show the region: 21 | VisualRegion.showRegion(player, regionUUID, region) 22 | // to hide the region 23 | VisualRegion.hideRegion(player, regionUUID) 24 | // to toggle on / off 25 | VisualRegion.toggleView(player, regionUUID, region) 26 | ``` 27 | 28 | If you want a "Visualize Region" button in your GUI, you can use the `VisualRegionItem`: 29 | 30 | ```kotlin 31 | VisualizeRegionItem(regionUuid) { region } 32 | ``` 33 | 34 | ## Dynamic Region 35 | 36 | A `DynamicRegion` is a type of region intended for use in tile-entities: Using a `MutableProvider` for the size, 37 | the region can be dynamically resized. It provides several gui-components to inspect, modify, and visualize the region. 38 | 39 | Using `TileEntity#storedRegion`, we can create a `DynamicRegion`: 40 | 41 | ```kotlin 42 | class ExampleTileEntity(pos: BlockPos, blockState: NovaBlockState, data: Compound) : TileEntity(pos, blockState, data) { 43 | 44 | private val region = storedRegion( 45 | "region", 46 | minSize = provider(1), // (1)! 47 | maxSize = provider(10), // (2)! 48 | defaultSize = 5, // (3)! 49 | createRegion = { size -> Region.surrounding(pos, size) } // (4)! 50 | ) 51 | 52 | @TileEntityMenuClass 53 | inner class ExampleTileEntityMenu(player: Player) : IndividualTileEntityMenu(player) { 54 | 55 | override val gui = Gui.builder() 56 | .setStructure( 57 | "v # # # # # # # #", 58 | "# # + # d # - # #", 59 | "# # # # # # # # #") 60 | .addIngredient('+', region.increaseSizeItem) 61 | .addIngredient('-', region.decreaseSizeItem) 62 | .addIngredient('d', region.displaySizeItem) 63 | .addIngredient('v', region.visualizeRegionItem) 64 | .build() 65 | 66 | } 67 | 68 | } 69 | ``` 70 | 71 | 1. The minimum size of the region. 72 | 2. The maximum size of the region. 73 | 3. The default size of the region when it is created. After that, the size can be changed and changes will be saved. 74 | 4. A lambda that creates the desired region based on the configured size. This is invoked every time the size is changed. -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/carver.md: -------------------------------------------------------------------------------- 1 | # Carvers 2 | 3 | !!! warning 4 | 5 | This worldgen page is still a work in progress. Some Json formats/code examples/features might be missing and will be 6 | added in the future. 7 | 8 | TODO - Check out the [Minecraft Wiki](https://minecraft.wiki/w/Custom_carver) in the meantime. -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/bamboo.md: -------------------------------------------------------------------------------- 1 | # Bamboo feature 2 | 3 | The `bamboo` feature is used to add bamboo to the world. 4 | 5 | ## Configuration 6 | 7 | Bamboo features can only be configured to have a specific probability of spawning a podzol disk under the bamboo. 8 | 9 | | Option | Type | Description | 10 | |---------------|-------------------------------------|------------------------------------------------------------------------| 11 | | `probability` | A `float` in the range $[0.0;1.0]$. | Determines the probability of spawning a podzol disk under the bamboo. | 12 | 13 | In code, the configuration is done via the `ProbabilityFeatureConfiguration` class. 14 | 15 | ## Example 16 | 17 | As an example, here's the configured and placed feature for the bamboo in the jungle. 18 | 19 | === "Kotlin" 20 | 21 | ```kotlin title="ConfiguredFeatures.kt" 22 | @OptIn(ExperimentalWorldGen::class) 23 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 24 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 25 | 26 | val BAMBOO_SOME_PODZOL = registerConfiguredFeature( 27 | "bamboo_some_podzol", 28 | Feature.BAMBOO, 29 | ProbabilityFeatureConfiguration(0.2f) // (1)! 30 | ) 31 | 32 | } 33 | ``` 34 | 35 | 1. Gives a $20\%$ chance of spawning a podzol disk under the bamboo. 36 | 37 | ```kotlin title="PlacedFeatures.kt" 38 | @OptIn(ExperimentalWorldGen::class) 39 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 40 | object PlacedFeatures: FeatureRegistry by ExampleAddon.registry { 41 | 42 | val BAMBOO_SOME_PODZOL = placedFeature("bamboo_some_podzol", ConfiguredFeatures.BAMBOO_SOME_PODZOL) 43 | .noiseBasedCount(170, 80.0, 0.3) // (1)! 44 | .inSquareSpread() // (2)! 45 | .moveToWorldSurface() // (3)! 46 | .biomeFilter() // (4)! 47 | .register() 48 | 49 | } 50 | ``` 51 | 52 | 1. Use noise to determine bamboo amount. 53 | See [Noise-based count placement](../placed-feature.md#minecraftnoise_based_count) for more information. 54 | 2. Spread the tries in a square. 55 | 3. Make sure to place the bamboo on the world surface. This call is equivalent to 56 | ```kotlin 57 | HeightmapPlacement.onHeightmap(Heightmap.Types.WORLD_SURFACE_WG) 58 | ``` 59 | 4. Only place the bamboo in biomes that have bamboo. 60 | 61 | === "Json" 62 | 63 | ```json title="configured_feature/bamboo_some_podzol.json" 64 | "type": "minecraft:bamboo", 65 | "config": { 66 | "probability": 0.2 // (1)! 67 | } 68 | ``` 69 | 70 | 1. Gives a $20\%$ chance of spawning a podzol disk under the bamboo. 71 | 72 | ```json title="placed_feature/bamboo.json" 73 | { 74 | "feature": "minecraft:bamboo_some_podzol", 75 | "placement": [ 76 | { 77 | "type": "minecraft:noise_based_count", // (1)! 78 | "noise_factor": 80.0, 79 | "noise_offset": 0.3, 80 | "noise_to_count_ratio": 160 81 | }, 82 | { 83 | "type": "minecraft:in_square" // (2)! 84 | }, 85 | { 86 | "type": "minecraft:heightmap", 87 | "heightmap": "WORLD_SURFACE_WG" // (3)! 88 | }, 89 | { 90 | "type": "minecraft:biome" // (4)! 91 | } 92 | ] 93 | } 94 | ``` 95 | 96 | 1. Use noise to determine bamboo amount. 97 | See [Noise-based count placement](../placed-feature.md#minecraftnoise_based_count) for more information. 98 | 2. Spread the tries in a square. 99 | 3. Make sure to place the bamboo on the world surface. 100 | 4. Only place the bamboo in biomes that have bamboo. 101 | 102 | ## Result 103 | 104 |

105 | Example 106 |

-------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/basalt-columns.md: -------------------------------------------------------------------------------- 1 | # Basalt columns feature 2 | 3 | The `basalt_columns` feature allows you to add basalt columns to the world. 4 | 5 | ## Configuration 6 | 7 | The `basalt_columns` feature has the following configuration options: 8 | 9 | | Option | Type | Description | 10 | |----------|--------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------| 11 | | `reach` | An [`IntProvider`](../../types/number-provider.md#intprovider). (Range limit in Json is $[0;3]$) | Determines the maximum radius from the center of the current column cluster. | 12 | | `height` | An `IntProvider`. (Range limit in Json is $[1;10]$) | Determines the maximum height of the current column cluster. (Actual height is $\text{height} + 1$ | 13 | 14 | In code, the configuration is done via the `ColumnFeatureConfiguration` class. 15 | 16 | ## Example 17 | 18 | As an example, here's the configured and placed feature for the large basalt columns in the nether. 19 | 20 | === "Kotlin" 21 | 22 | ```kotlin title="ConfiguredFeatures.kt" 23 | @OptIn(ExperimentalWorldGen::class) 24 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 25 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 26 | 27 | val LARGE_BASALT_COLUMNS = registerConfiguredFeature( 28 | "large_basalt_columns", 29 | Feature.BASALT_COLUMNS, 30 | ColumnFeatureConfiguration(UniformInt.of(2, 3), UniformInt.of(5, 10)) // (1)! 31 | ) 32 | 33 | } 34 | ``` 35 | 36 | 1. Randomly chooses a radius between 2 and 3 and a height between 5 and 10. 37 | 38 | ```kotlin title="PlacedFeatures.kt" 39 | @OptIn(ExperimentalWorldGen::class) 40 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 41 | object PlacedFeatures: FeatureRegistry by ExampleAddon.registry { 42 | 43 | val LARGE_BASALT_COLUMNS = placedFeature("large_basalt_columns", ConfiguredFeatures.LARGE_BASALT_COLUMNS) 44 | .countOnEveryLayer(2) // (1)! 45 | .biomeFilter() // (2)! 46 | .register() 47 | 48 | } 49 | ``` 50 | 51 | 1. Spreads the basalt columns to multiple layers. 52 | 2. Only place the columns in biomes that have basalt. 53 | 54 | === "Json" 55 | 56 | ```json title="configured_feature/large_basalt_columns.json" 57 | { 58 | "type": "minecraft:basalt_columns", 59 | "config": { 60 | "reach": { // (1)! 61 | "type": "minecraft:uniform", 62 | "value": { 63 | "max_inclusive": 3, 64 | "min_inclusive": 2 65 | } 66 | }, 67 | "height": { // (2)! 68 | "type": "minecraft:uniform", 69 | "value": { 70 | "max_inclusive": 10, 71 | "min_inclusive": 5 72 | } 73 | } 74 | } 75 | } 76 | ``` 77 | 78 | 1. Randomly chooses a radius between 2 and 3. 79 | 2. Randomly chooses a height between 5 and 10. 80 | 81 | ```json title="placed_feature/large_basalt_columns.json" 82 | { 83 | "feature": "minecraft:large_basalt_columns", 84 | "placement": [ 85 | { 86 | "type": "minecraft:count_on_every_layer", // (1)! 87 | "count": 2 88 | }, 89 | { 90 | "type": "minecraft:biome" // (2)! 91 | } 92 | ] 93 | } 94 | ``` 95 | 96 | 1. Spreads the basalt columns to multiple layers. 97 | 2. Only place the columns in biomes that have basalt. 98 | 99 | ## Result 100 | 101 |

102 | Example 103 |

-------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/block-pile.md: -------------------------------------------------------------------------------- 1 | # Block pile feature 2 | 3 | The `block_pile` can be used to place piles of blocks (for example hay/melons in villages) in the world. 4 | 5 | ## Configuration 6 | 7 | A block pile feature only has the `state_provider` option: 8 | 9 | | Option | Type | Description | 10 | |------------------|----------------------------------------------------------------|--------------------------------------| 11 | | `state_provider` | A [`BlockStateProvider`](../../types/block-state-provider.md) | The block state to use for the pile. | 12 | 13 | In code, the `BlockPileConfiguration` class is used to configure the feature. 14 | 15 | ## Example 16 | 17 | As an example, here's the placed and configured feature used to place piles of hay bales in villages. 18 | 19 | === "Kotlin" 20 | 21 | ```kotlin title="ConfiguredFeatures.kt" 22 | @OptIn(ExperimentalWorldGen::class) 23 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 24 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 25 | 26 | val PILE_HAY = registerConfiguredFeature( 27 | "pile_hay", 28 | Feature.BLOCK_PILE, 29 | BlockPileConfiguration(RotatedBlockProvider(Blocks.HAY_BLOCK)) // (1)! 30 | ) 31 | 32 | } 33 | ``` 34 | 35 | 1. Randomly rotate the hay bales. 36 | 37 | ```kotlin title="PlacedFeatures.kt" 38 | @OptIn(ExperimentalWorldGen::class) 39 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 40 | object PlacedFeatures: FeatureRegistry by ExampleAddon.registry { 41 | 42 | val PILE_HAY = placedFeature("pile_hay", ConfiguredFeatures.PILE_HAY).register() // (1)! 43 | 44 | } 45 | ``` 46 | 47 | 1. The feature does all the location resolving itself, so no extra `PlacementModifiers` are needed. 48 | 49 | === "Json" 50 | 51 | ```json title="configured_feature/pile_hay.json" 52 | { 53 | "type": "minecraft:block_pile", 54 | "config": { 55 | "state_provider": { 56 | "type": "minecraft:rotated_block_provider", 57 | "state": { 58 | "Name": "minecraft:hay_block", 59 | "Properties": { 60 | "axis": "y" // (1)! 61 | } 62 | } 63 | } 64 | } 65 | } 66 | ``` 67 | 68 | 1. Randomly rotate the hay bales. 69 | 70 | ```json title="placed_feature/pile_hay.json" 71 | { 72 | "feature": "minecraft:pile_hay", 73 | "placement": [] // (1)! 74 | } 75 | ``` 76 | 77 | 1. The feature does all the location resolving itself. 78 | 79 | ## Result 80 | 81 | === "Alone" 82 | 83 |

84 | Example 85 |

86 | 87 | === "Naturally generated" 88 | 89 |

90 | Example 91 |

-------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/delta.md: -------------------------------------------------------------------------------- 1 | # Delta feature 2 | 3 | The `delta_feature` is a 1-block deep sheet of a block randomly surrounded by a block (rim). It's normally used to generate 4 | the deltas in the basalt deltas biome. 5 | 6 | ## Configuration 7 | 8 | The following configuration options are available: 9 | 10 | | Option | Type | Description | 11 | |------------|---------------------------------------------------------------------------------------------------|---------------------------------------------------------------------| 12 | | `contents` | A [`BlockState`](../../types/block-state.md) | The block state to use inside of the delta. | 13 | | `rim` | A `BlockState` | The block state to use for the rim. | 14 | | `size` | An [`IntProvider`](../../types/number-provider.md#intprovider). (Range limit in Json is $[0;16]$) | Determines the maximum radius from the center of the current delta. | 15 | | `rim_size` | An `IntProvider`. (Range limit in Json is $[0;16]$) | Determines the size of the rim. | 16 | 17 | In code, the `DeltaFeatureConfiguration` class is used to configure the feature. 18 | 19 | ## Example 20 | 21 | As an example, here's the placed and configured feature used to place deltas in the basalt deltas biome. 22 | 23 | === "Kotlin" 24 | 25 | ```kotlin title="ConfiguredFeatures.kt" 26 | @OptIn(ExperimentalWorldGen::class) 27 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 28 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 29 | 30 | val DELTA = registerConfiguredFeature( 31 | "delta", 32 | Feature.DELTA_FEATURE, 33 | DeltaFeatureConfiguration( 34 | Blocks.LAVA.defaultBlockState(), // contents 35 | Blocks.MAGMA_BLOCK.defaultBlockState(), // rim 36 | UniformInt.of(3, 7), // size (1) 37 | UniformInt.of(0, 2) // rim_size 38 | ) 39 | ) 40 | 41 | } 42 | ``` 43 | 44 | 1. Random `int` in the range $[3;7]$. 45 | 46 | ```kotlin title="PlacedFeatures.kt" 47 | @OptIn(ExperimentalWorldGen::class) 48 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 49 | object PlacedFeatures: FeatureRegistry by ExampleAddon.registry { 50 | 51 | val DELTA = placedFeature("delta", ConfiguredFeatures.DELTA) 52 | .countOnEveryLayer(40) // (1)! 53 | .biomeFilter() // (2)! 54 | .register() 55 | 56 | } 57 | ``` 58 | 59 | 1. Spreads the deltas to multiple layers. 60 | 2. Only generate the feature if the center pos hasn't moved to another biome that doesn't have the configured feature. 61 | 62 | === "Json" 63 | 64 | ```json title="configured_feature/delta.json" 65 | { 66 | "type": "minecraft:delta_feature", 67 | "config": { 68 | "contents": { 69 | "Name": "minecraft:lava", 70 | "Properties": { 71 | "level": "0" 72 | } 73 | }, 74 | "rim": { 75 | "Name": "minecraft:magma_block" 76 | }, 77 | "rim_size": { 78 | "type": "minecraft:uniform", // (1)! 79 | "value": { 80 | "max_inclusive": 2, 81 | "min_inclusive": 0 82 | } 83 | }, 84 | "size": { 85 | "type": "minecraft:uniform", 86 | "value": { 87 | "max_inclusive": 7, 88 | "min_inclusive": 3 89 | } 90 | } 91 | } 92 | } 93 | ``` 94 | 95 | 1. Random `int` in the range $[2;0]$. 96 | 97 | ```json title="placed_feature/delta.json" 98 | { 99 | "feature": "minecraft:delta", 100 | "placement": [ 101 | { 102 | "type": "minecraft:count_on_every_layer", // (1)! 103 | "count": 40 104 | }, 105 | { 106 | "type": "minecraft:biome" // (2)! 107 | } 108 | ] 109 | } 110 | ``` 111 | 112 | 1. Spreads the deltas to multiple layers. 113 | 2. Only generate the feature if the center pos hasn't moved to another biome that doesn't have the configured feature. 114 | 115 | ## Result 116 | 117 | ![Example](https://i.imgur.com/Bfy18m5.gif) -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/end-gateway.md: -------------------------------------------------------------------------------- 1 | # End gateway feature 2 | 3 | The `end_gateway` feature can be used to place end gateways in the world. 4 | 5 | ## Configuration 6 | 7 | An end gateway feature has the following configuration options: 8 | 9 | | Option | Type | Description | 10 | |-------------------|---------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------| 11 | | `exit` (optional) | A `BlockPos` (In Json, the `BlockPos` is represented via an array of coordinates. First element is the x coordinate and so on.) | The exit location of the end gateway. | 12 | | `exact` | `boolean` | Whether entities should be teleported to the exact exit location. | 13 | 14 | In code, the `EndGatewayConfiguration` class is used to configure the feature. 15 | 16 | ## Example 17 | 18 | Here's the configured and placed feature for the vanilla return end gateway: 19 | 20 | === "Kotlin" 21 | 22 | ```kotlin title="ConfiguredFeatures.kt" 23 | @OptIn(ExperimentalWorldGen::class) 24 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 25 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 26 | 27 | val END_GATEWAY_RETURN = registerConfiguredFeature( 28 | "end_gateway_return", 29 | Feature.END_GATEWAY, 30 | EndGatewayConfiguration.knownExit( // (1)! 31 | ServerLevel.END_SPAWN_POINT, // (2)! 32 | true // (3)! 33 | ) 34 | ) 35 | 36 | } 37 | ``` 38 | 39 | 1. If the exit location is still unknown during registration (for example for random teleportation), use 40 | ```kotlin 41 | EndGatewayConfiguration.delayedExitSearch() 42 | ``` 43 | 2. Constant for the end spawn point at $(100|50|0)$. 44 | 3. Entities should be teleported to the exact exit location. 45 | 46 | ```kotlin title="PlacedFeatures.kt" 47 | @OptIn(ExperimentalWorldGen::class) 48 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 49 | object PlacedFeatures : FeatureRegistry by ExampleAddon.registry { 50 | 51 | val END_GATEWAY_RETURN = placedFeature("end_gateway_return", ConfiguredFeatures.END_GATEWAY_RETURN) 52 | .rarityFilter(700) // (1)! 53 | .inSquareSpread() // (2)! 54 | .moveToMotionBlocking() // (3)! 55 | .randomVerticalOffset(UniformInt.of(3, 9)) // (4)! 56 | .biomeFilter() // (5)! 57 | .register() 58 | 59 | } 60 | ``` 61 | 62 | 1. Give the end gateway a chance of $^1/_{700}$ to spawn. Or in other words, the end gateway will spawn in 1 out of 700 chunks. 63 | 2. Randomly offset the gateways in a square. 64 | 3. Move the gateways to the surface. The call is equivalent to 65 | ```kotlin 66 | HeightmapPlacement.onHeightmap(Heightmap.Types.MOTION_BLOCKING) 67 | ``` 68 | 4. Randomly offset the gateways in the y direction. 69 | 5. Only place the gateways in biomes that have end gateways 70 | 71 | === "Json" 72 | 73 | ```json title="configured_feature/end_gateway_return.json" 74 | { 75 | "type": "minecraft:end_gateway", 76 | "config": { 77 | "exact": true, 78 | "exit": [ // (1)! 79 | 100, 80 | 50, 81 | 0 82 | ] 83 | } 84 | } 85 | ``` 86 | 87 | 1. The end spawn point is always at $(100|50|0)$ 88 | 89 | ```json title="placed_feature/end_gateway_return.json" 90 | { 91 | "feature": "minecraft:end_gateway_return", 92 | "placement": [ 93 | { 94 | "type": "minecraft:rarity_filter", // (1)! 95 | "chance": 700 96 | }, 97 | { 98 | "type": "minecraft:in_square" // (2)! 99 | }, 100 | { 101 | "type": "minecraft:heightmap", // (3)! 102 | "heightmap": "MOTION_BLOCKING" 103 | }, 104 | { 105 | "type": "minecraft:random_offset", // (4)! 106 | "xz_spread": 0, 107 | "y_spread": { 108 | "type": "minecraft:uniform", 109 | "value": { 110 | "max_inclusive": 9, 111 | "min_inclusive": 3 112 | } 113 | } 114 | }, 115 | { 116 | "type": "minecraft:biome" // (5)! 117 | } 118 | ] 119 | } 120 | ``` 121 | 122 | 1. Give the end gateway a chance of $^1/_{700}$ to spawn. Or in other words, the end gateway will spawn in 1 out of 700 chunks. 123 | 2. Randomly offset the gateways in a square. 124 | 3. Move the gateways to the surface. 125 | 4. Randomly offset the gateways in the y direction. 126 | 5. Only place the gateways in biomes that have end gateways. 127 | 128 | ## Result 129 | 130 |

131 | Example 132 |

-------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/fill-layer.md: -------------------------------------------------------------------------------- 1 | # Fill layer feature 2 | 3 | The `fill_layer` feature can be used to fill an entire 16x16 chunk area with a single block. 4 | 5 | ## Configuration 6 | 7 | The following configuration options are available: 8 | 9 | 10 | | Option | Type | Description | 11 | |----------|------------------------------------------------|---------------------------------------------------------------------| 12 | | `height` | An `int`. (Range limit in Json is $[0;4064 ]$) | The height of the layer to fill (starting at the min build height). | 13 | | `state` | A [`BlockState`](../../types/block-state.md) | The block state to use for the layer. | 14 | 15 | In code, the `LayerConfiguration` class is used to configure the feature. 16 | 17 | ## Example 18 | 19 | As an example, here's a configured- and placed feature to add a layer of grass on top of a default flat world. 20 | 21 | === "Kotlin" 22 | 23 | ```kotlin title="ConfiguredFeatures.kt" 24 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 25 | @OptIn(ExperimentalWorldGen::class) 26 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 27 | 28 | val FILL_LAYER_GRASS = registerConfiguredFeature( 29 | "fill_layer_grass", 30 | Feature.FILL_LAYER, 31 | LayerConfiguration(4, Blocks.GRASS.defaultBlockState()) // (1)! 32 | ) 33 | 34 | } 35 | ``` 36 | 37 | 1. Add a layer of grass at height 4. 38 | 39 | ```kotlin title="PlacedFeatures.kt" 40 | @OptIn(ExperimentalWorldGen::class) 41 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 42 | object PlacedFeatures : FeatureRegistry by ExampleAddon.registry { 43 | 44 | val FILL_LAYER_GRASS = placedFeature("fill_layer_grass", ConfiguredFeatures.FILL_LAYER_GRASS).register() 45 | 46 | } 47 | ``` 48 | 49 | === "Json" 50 | 51 | ```json title="configured_feature/fill_layer_grass.json" 52 | { 53 | "height": 4, 54 | "state": { 55 | "Name": "minecraft:grass" 56 | } 57 | } 58 | ``` 59 | 60 | ```json title="placed_feature/fill_layer_grass.json" 61 | { 62 | "feature": "minecraft:fill_layer_grass", 63 | "placement": [] 64 | } 65 | ``` -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/forest-rock.md: -------------------------------------------------------------------------------- 1 | # Forest rock feature 2 | 3 | The `forest_rock` feature can be used to generate small $3x3$ rocks in the world. 4 | 5 | ## Configuration 6 | 7 | Only the block state used for the rocks is configurable: 8 | 9 | | Option | Type | Description | 10 | |---------|-----------------------------------------------|--------------------------------------| 11 | | `state` | A [`BlockState`](../../types/block-state.md) | The block state to use for the rock. | 12 | 13 | In code, the `BlockStateConfiguration` class is used to configure the feature. 14 | 15 | ## Example 16 | 17 | === "Kotlin" 18 | 19 | ```kotlin title="ConfiguredFeatures.kt" 20 | @OptIn(ExperimentalWorldGen::class) 21 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 22 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 23 | 24 | val FOREST_ROCK = registerConfiguredFeature( 25 | "forest_rock", 26 | Feature.FOREST_ROCK, 27 | BlockStateConfiguration(Blocks.MOSSY_COBBLESTONE.defaultBlockState()) 28 | ) 29 | 30 | } 31 | ``` 32 | 33 | ```kotlin title="PlacedFeatures.kt" 34 | @OptIn(ExperimentalWorldGen::class) 35 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 36 | object PlacedFeatures : FeatureRegistry by ExampleAddon.registry { 37 | 38 | val FOREST_ROCK = placedFeature("forest_rock", ConfiguredFeatures.FOREST_ROCK) 39 | .count(2) // (1)! 40 | .inSquareSpread() // (2)! 41 | .moveToMotionBlocking() // (3)! 42 | .biomeFilter() // (4)! 43 | .register() 44 | 45 | } 46 | ``` 47 | 48 | 1. Generate 2 rocks per chunk. 49 | 2. Randomly offset the x- and z-coordinates of the rock. 50 | 3. Set the y-coordinate of the rock to the highest motion-blocking block. The call is equivalent to 51 | ```kotlin 52 | HeightmapPlacement.onHeightmap(Heightmap.Types.MOTION_BLOCKING) 53 | ``` 54 | 4. Only generate the feature if the center pos hasn't moved to another biome that doesn't have the `forest_rock` feature. 55 | 56 | === "Json" 57 | 58 | ```json title="configured_feature/forest_rock.json" 59 | { 60 | "type": "minecraft:forest_rock", 61 | "config": { 62 | "state": { 63 | "Name": "minecraft:mossy_cobblestone" 64 | } 65 | } 66 | } 67 | ``` 68 | 69 | ```json title="placed_feature/forest_rock.json" 70 | { 71 | "feature": "minecraft:forest_rock", 72 | "placement": [ 73 | { 74 | "type": "minecraft:count", 75 | "count": 2 // (1)! 76 | }, 77 | { 78 | "type": "minecraft:in_square" // (2)! 79 | }, 80 | { 81 | "type": "minecraft:heightmap", 82 | "heightmap": "MOTION_BLOCKING" // (3)! 83 | }, 84 | { 85 | "type": "minecraft:biome" // (4)! 86 | } 87 | ] 88 | } 89 | ``` 90 | 91 | 1. Generate 2 rocks per chunk. 92 | 2. Randomly offset the x- and z-coordinates of the rock. 93 | 3. Set the y-coordinate of the rock to the highest motion-blocking block. 94 | 4. Only generate the feature if center pos hasn't moved to another biome that doesn't have the `forest_rock` feature. 95 | 96 | ## Result 97 | 98 | === "Alone" 99 | 100 |

101 | Example 102 |

103 | 104 | === "Naturally generated" 105 | 106 |

107 | Example 108 |

-------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/fossil.md: -------------------------------------------------------------------------------- 1 | TODO -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/huge-fungus.md: -------------------------------------------------------------------------------- 1 | # Huge fungus feature 2 | 3 | The `huge_fungus` feature can be used to place huge fungi in the world (used for warped and crimson forests in vanilla). 4 | 5 | ## Configuration 6 | 7 | The `huge_fungus` feature has the following configuration options: 8 | 9 | | Option | Type | Description | 10 | |---------------------------------------------------|----------------------------------------------|---------------------------------------------------------------------------------------------------| 11 | | `valid_base_block` | A [`BlockState`](../../types/block-state.md) | The block state that needs to be present below the fungus. | 12 | | `stem_state` | A `BlockState` | The block state to use for the stem of the fungus. | 13 | | `hat_state` | A `BlockState` | The block state to use for the hat of the fungus. | 14 | | `decor_state` | A `BlockState` | The block state to randomly place under the hat as decoration. (For example shroomlight) | 15 | | `planted` (optional in Json, defaults to `false`) | A `boolean` | If set to `false`, can only replace `PLANT` material blocks and doesn't drop items when replaced. | 16 | 17 | In code, the `HugeFungusConfiguration` class is used to configure the feature. 18 | 19 | ## Example 20 | 21 | As an example, here's the configured- and placed feature for the warped fungus: 22 | 23 | === "Kotlin" 24 | 25 | ```kotlin title="ConfiguredFeatures.kt" 26 | @OptIn(ExperimentalWorldGen::class) 27 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 28 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 29 | 30 | val WARPED_FUNGUS = registerConfiguredFeature( 31 | "warped_fungus", 32 | Feature.HUGE_FUNGUS, 33 | HugeFungusConfiguration( 34 | Blocks.WARPED_NYLIUM.defaultBlockState(), // (1)! 35 | Blocks.WARPED_STEM.defaultBlockState(), // (2)! 36 | Blocks.WARPED_WART_BLOCK.defaultBlockState(), // (3)! 37 | Blocks.SHROOMLIGHT.defaultBlockState(), // (4)! 38 | false // (5)! 39 | ) 40 | ) 41 | 42 | } 43 | ``` 44 | 45 | 1. Only place the fungus on warped nylium. 46 | 2. The stem of the fungus. 47 | 3. The hat of the fungus. 48 | 4. Randomly placed shroomlight under the hat. 49 | 5. The fungus is automatically generated, so it shouldn't drop items when another fungus grows into it. 50 | 51 | ```kotlin title="PlacedFeatures.kt" 52 | @OptIn(ExperimentalWorldGen::class) 53 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 54 | object PlacedFeatures : FeatureRegistry by ExampleAddon.registry { 55 | 56 | val WARPED_FUNGUS = placedFeature("warped_fungus", ConfiguredFeatures.WARPED_FUNGUS) 57 | .countOnEveryLayer(8) 58 | .biomeFilter() 59 | .register() 60 | 61 | } 62 | ``` 63 | 64 | === "Json" 65 | 66 | ```json title="configured_feature/warped_fungus.json" 67 | { 68 | "type": "minecraft:huge_fungus", 69 | "config": { 70 | "decor_state": { 71 | "Name": "minecraft:shroomlight" // (1)! 72 | }, 73 | "hat_state": { 74 | "Name": "minecraft:warped_wart_block" 75 | }, 76 | "stem_state": { 77 | "Name": "minecraft:warped_stem", 78 | "Properties": { 79 | "axis": "y" 80 | } 81 | }, 82 | "valid_base_block": { 83 | "Name": "minecraft:warped_nylium" // (2)! 84 | }, 85 | "planted": false // (3)! 86 | } 87 | } 88 | ``` 89 | 90 | 1. Randomly placed shroomlight under the hat. 91 | 2. Only place the fungus on warped nylium. 92 | 3. The fungus is automatically generated, so it shouldn't drop items when another fungus grows into it. 93 | 94 | ```json title="placed_feature/warped_fungi.json" 95 | { 96 | "feature": "minecraft:warped_fungus", 97 | "placement": [ 98 | { 99 | "type": "minecraft:count_on_every_layer", 100 | "count": 8 101 | }, 102 | { 103 | "type": "minecraft:biome" 104 | } 105 | ] 106 | } 107 | ``` 108 | 109 | ## Result 110 | 111 | === "Alone" 112 | 113 |

114 | Example 115 |

116 | 117 | === "Naturally generated" 118 | 119 |

120 | Example 121 |

-------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/huge-mushrooms.md: -------------------------------------------------------------------------------- 1 | # Huge mushroom features 2 | 3 | The `huge_brown_mushroom ` and `huge_red_mushroom` features can be used to add huge mushrooms to a biome. 4 | 5 | ## Configuration 6 | 7 | | Option | Type | Description | 8 | |----------------------------------------------------|---------------------------------------------------------------|-----------------------------------------------------------| 9 | | `cap_provider` | A [`BlockStateProvider`](../../types/block-state-provider.md) | Determines the block to use for the cap of the mushroom. | 10 | | `stem_provider` | A `BlockStateProvider` | Determines the block to use for the stem of the mushroom. | 11 | | `foliage_radius` (optional in Json, defaults to 2) | An `int` | Determines the radius of the cap. | 12 | 13 | In code, the `HugeMushroomFeatureConfiguration` class is used to configure the feature. 14 | 15 | ## Example 16 | 17 | As an example, here's the configured- and placed feature for the default huge red mushroom 18 | 19 | === "Kotlin" 20 | 21 | ```kotlin title="ConfiguredFeatures.kt" 22 | @OptIn(ExperimentalWorldGen::class) 23 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 24 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 25 | 26 | val HUGE_RED_MUSHROOM = registerConfiguredFeature( 27 | "huge_red_mushroom", 28 | Feature.HUGE_RED_MUSHROOM, 29 | HugeMushroomFeatureConfiguration( 30 | BlockStateProvider.simple(Blocks.RED_MUSHROOM_BLOCK.defaultBlockState().setValue(HugeMushroomBlock.DOWN, false) as BlockState), // (1)! 31 | BlockStateProvider.simple((Blocks.MUSHROOM_STEM.defaultBlockState().setValue(HugeMushroomBlock.UP, false) as BlockState).setValue(HugeMushroomBlock.DOWN, false) as BlockState), // (2)! 32 | 2 // (3)! 33 | ) 34 | ) 35 | 36 | } 37 | ``` 38 | 39 | 1. Use a mushroom block with the `down` property set to `false` as the cap. 40 | 2. Use a mushroom stem with the `up` and `down` properties set to `false` as the stem. 41 | 3. Use a radius of 2 for the cap. 42 | 43 | ```kotlin title="PlacedFeatures.kt" 44 | @OptIn(ExperimentalWorldGen::class) 45 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 46 | object PlacedFeatures : FeatureRegistry by ExampleAddon.registry { 47 | 48 | val HUGE_RED_MUSHROOM = placedFeature("huge_red_mushroom", ConfiguredFeatures.HUGE_RED_MUSHROOM).register() 49 | 50 | } 51 | ``` 52 | 53 | === "Json" 54 | 55 | ```json title="configured_feature/huge_red_mushroom.json" 56 | { 57 | "type": "minecraft:huge_red_mushroom", 58 | "config": { 59 | "cap_provider": { 60 | "type": "minecraft:simple_state_provider", 61 | "state": { 62 | "Name": "minecraft:red_mushroom_block", 63 | "Properties": { 64 | "down": "false", 65 | "east": "true", 66 | "north": "true", 67 | "south": "true", 68 | "up": "true", 69 | "west": "true" 70 | } 71 | } 72 | }, 73 | "stem_provider": { 74 | "type": "minecraft:simple_state_provider", 75 | "state": { 76 | "Name": "minecraft:mushroom_stem", 77 | "Properties": { 78 | "down": "false", 79 | "east": "true", 80 | "north": "true", 81 | "south": "true", 82 | "up": "false", 83 | "west": "true" 84 | } 85 | } 86 | }, 87 | "foliage_radius": 2 88 | } 89 | } 90 | ``` 91 | 92 | ```json title="placed_feature/huge_red_mushroom.json" 93 | { 94 | "feature": { 95 | "feature": "minecraft:huge_red_mushroom", 96 | "placement": [] 97 | } 98 | ``` 99 | 100 | ## Result 101 | 102 | === "Alone" 103 | 104 |

105 | Example 106 |

107 | 108 | === "Naturally generated" 109 | 110 |

111 | Example 112 |

-------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/iceberg.md: -------------------------------------------------------------------------------- 1 | # Iceberg feature 2 | 3 | The `iceberg` feature allows you to add icebergs to the world. 4 | 5 | ## Configuration 6 | 7 | The `iceberg` feature only has one configuration option: 8 | 9 | | Option | Type | Description | 10 | |---------|----------------------------------------------|------------------------------------------| 11 | | `state` | A [`BlockState`](../../types/block-state.md) | The block state to use for the icebergs. | 12 | 13 | In code, the `BlockStateConfiguration` class is used to configure the feature. 14 | 15 | ## Example 16 | 17 | As an example, here's the configured and placed feature for the blue icebergs in the deep frozen ocean. 18 | 19 | === "Kotlin" 20 | 21 | ```kotlin title="ConfiguredFeatures.kt" 22 | @OptIn(ExperimentalWorldGen::class) 23 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 24 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 25 | 26 | val ICEBERG_BLUE = registerConfiguredFeature( 27 | "iceberg_blue", 28 | Feature.ICEBERG, 29 | BlockStateConfiguration(Blocks.BLUE_ICE.defaultBlockState()) 30 | ) 31 | 32 | } 33 | ``` 34 | 35 | ```kotlin title="PlacedFeatures.kt" 36 | @OptIn(ExperimentalWorldGen::class) 37 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 38 | object PlacedFeatures : FeatureRegistry by ExampleAddon.registry { 39 | 40 | val ICEBERG_BLUE = placedFeature("iceberg_blue", ConfiguredFeatures.ICEBERG_BLUE) 41 | .rarityFilter(200) // (1)! 42 | .inSquareSpread() // (2)! 43 | .biomeFilter() // (3)! 44 | .register() 45 | 46 | } 47 | ``` 48 | 49 | 1. Only place an iceberg every 200 chunks. 50 | 2. Randomly offset the iceberg horizontally. 51 | 3. Only place the iceberg if the location hasn't moved to a biome without icebergs. 52 | 53 | === "Json" 54 | 55 | ```json title="configured_feature/iceberg_blue.json" 56 | { 57 | "type": "minecraft:iceberg", 58 | "config": { 59 | "state": { 60 | "Name": "minecraft:blue_ice" 61 | } 62 | } 63 | } 64 | ``` 65 | 66 | ```json title="placed_feature/iceberg_blue.json" 67 | { 68 | "feature": "minecraft:iceberg_blue", 69 | "placement": [ 70 | { 71 | "type": "minecraft:rarity_filter", 72 | "chance": 200 // (1)! 73 | }, 74 | { 75 | "type": "minecraft:in_square" // (2)! 76 | }, 77 | { 78 | "type": "minecraft:biome" // (3)! 79 | } 80 | ] 81 | } 82 | ``` 83 | 84 | 1. Only place an iceberg every 200 chunks. 85 | 2. Randomly offset the iceberg horizontally. 86 | 3. Only place the iceberg if the location hasn't moved to a biome without icebergs. 87 | 88 | ## Result 89 | 90 | ![Example](https://i.imgur.com/hRVHhcb.gif) -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/nether-forest-vegetation.md: -------------------------------------------------------------------------------- 1 | # Nether forest vegetation feature 2 | 3 | !!! warning "Hardcoded block check" 4 | 5 | This feature has a hardcoded check for the surface block being `nylium` and will not work on other blocks 6 | 7 | The `nether_forest_vegetation` feature is a feature that randomly spreads a specific block on the surface. 8 | 9 | ## Configuration 10 | 11 | The nether forest vegetation feature has the following configuration options: 12 | 13 | | Option | Type | Description | 14 | |------------------|----------------------------------------------------------------|-----------------------------------------------------| 15 | | `state_provider` | A [`BlockStateProvider`](../../types/block-state-provider.md). | The block state to use for the vegetation. | 16 | | `spread_width` | A positive `int`. | The width of the area to spread the vegetation in. | 17 | | `spread_height` | A positive `int`. | The height of the area to spread the vegetation in. | 18 | 19 | In code, the `NetherForestVegetationConfig` class is used to configure the feature. 20 | 21 | ## Example 22 | 23 | As an example, here's the configured- and placed feature to spread nether sprouts in the nether forest biomes. 24 | 25 | === "Kotlin" 26 | 27 | ```kotlin title="ConfiguredFeatures.kt" 28 | @OptIn(ExperimentalWorldGen::class) 29 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 30 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 31 | 32 | val NETHER_SPROUTS = registerConfiguredFeature( 33 | "nether_sprouts", 34 | Feature.NETHER_FOREST_VEGETATION, 35 | NetherForestVegetationConfig( 36 | BlockStateProvider.simple(Blocks.NETHER_SPROUTS), // block used for the vegetation 37 | 8, // spreadWidth 38 | 4 // spreadHeight 39 | ) 40 | ) 41 | 42 | } 43 | ``` 44 | 45 | ```kotlin title="PlacedFeatures.kt" 46 | @OptIn(ExperimentalWorldGen::class) 47 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 48 | object PlacedFeatures : FeatureRegistry by ExampleAddon.registry { 49 | 50 | val NETHER_SPROUTS = placedFeature("nether_sprouts", ConfiguredFeatures.NETHER_SPROUTS) 51 | .countOnEveryLayer(4) // (1)! 52 | .biomeFilter() // (2)! 53 | .register() 54 | 55 | } 56 | ``` 57 | 58 | 1. Make sure to place the nether sprouts on every layer if multiple exist in the biome. 59 | 2. Only place the sprouts in the nether forest biomes. 60 | 61 | === "Json" 62 | 63 | ```json title="configured_feature/nether_sprouts.json" 64 | { 65 | "type": "minecraft:nether_forest_vegetation", 66 | "config": { 67 | "state_provider": { 68 | "type": "minecraft:simple_state_provider", 69 | "state": { 70 | "Name": "minecraft:nether_sprouts" 71 | } 72 | }, 73 | "spread_height": 4, 74 | "spread_width": 8 75 | } 76 | } 77 | ``` 78 | 79 | ```json title="placed_feature/nether_sprouts.json" 80 | { 81 | "feature": "minecraft:nether_sprouts", 82 | "placement": [ 83 | { 84 | "type": "minecraft:count_on_every_layer", // (1)! 85 | "count": 4 86 | }, 87 | { 88 | "type": "minecraft:biome" // (2)! 89 | } 90 | ] 91 | } 92 | ``` 93 | 94 | 1. Make sure to place the nether sprouts on every layer if multiple exist in the biome. 95 | 2. Only place the sprouts in the nether forest biomes. -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/pointed-dripstone.md: -------------------------------------------------------------------------------- 1 | # Pointed dripstone feature 2 | 3 | The `pointed_dripstone` feature can be used to add pointed dripstone to the world. 4 | 5 | ## Configuration 6 | 7 | The `pointed_dripstone` feature has the following configuration options: 8 | 9 | | Option | Type | Description | 10 | |----------------------------------------------------------------------|-------------------------------------|-----------------------------------------------------------------| 11 | | `chance_of_taller_dripstone` (optional in Json, defaults to `0.2`) | A `float` in the range $[0.0;1.0]$. | Determines the chance of a dripstone being taller than 1 block. | 12 | | `chance_of_directional_spread` (optional in Json, defaults to `0.7`) | A `float` in the range $[0.0;1.0]$. | Determines the chance of a dripstone spreading horizontally. | 13 | | `chance_of_spread_radius2` (optional in Json, defaults to `0.5`) | A `float` in the range $[0.0;1.0]$. | Determines the chance of a dripstone spreading 2 blocks away. | 14 | | `chance_of_spread_radius3` (optional in Json, defaults to `0.5`) | A `float` in the range $[0.0;1.0]$. | Determines the chance of a dripstone spreading 3 blocks away. | 15 | 16 | In code, the `PointedDripstoneConfiguration` class is used to configure the feature. 17 | 18 | ## Example 19 | 20 | Minecraft uses a [`simple_random_selector`](simple-random-selector.md) feature to actually place pointed dripstone. Here's 21 | one of the features used to place upwards pointing dripstone. 22 | 23 | === "Kotlin" 24 | 25 | ```kotlin title="ConfiguredFeatures.kt" 26 | @OptIn(ExperimentalWorldGen::class) 27 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 28 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 29 | 30 | val POINTED_DRIPSTONE = registerConfiguredFeature( 31 | "pointed_dripstone", 32 | Feature.POINTED_DRIPSTONE, 33 | PointedDripstoneConfiguration( 34 | 0.2F, // chance_of_taller_dripstone 35 | 0.7F, // chance_of_directional_spread 36 | 0.5F, // chance_of_spread_radius2 37 | 0.5F // chance_of_spread_radius3 38 | ) 39 | ) 40 | 41 | } 42 | ``` 43 | 44 | ```kotlin title="PlacedFeatures.kt" 45 | @OptIn(ExperimentalWorldGen::class) 46 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 47 | object PlacedFeatures : FeatureRegistry by ExampleAddon.registry { 48 | 49 | val POINTED_DRIPSTONE = placedFeature("pointed_dripstone", ConfiguredFeatures.POINTED_DRIPSTONE) 50 | .environmentScan( // (1)! 51 | Direction.DOWN, 52 | BlockPredicate.solid(), 53 | BlockPredicate.ONLY_IN_AIR_OR_WATER_PREDICATE, // (2)! 54 | 12 // max_steps 55 | ) 56 | .randomVerticalOffset(1) // (3)! 57 | .register() 58 | 59 | } 60 | ``` 61 | 62 | 1. Searches downwards for a solid block. 63 | 2. Only place the dripstone if the block below is air or water. The static constant is equivalent to 64 | ```kotlin 65 | BlockPredicate.matchesBlocks(Blocks.AIR, Blocks.WATER) 66 | ``` 67 | 3. Makes sure to place the dripstone on top of the solid block. 68 | 69 | === "Json" 70 | 71 | ```json title="configured_feature/pointed_dripstone.json" 72 | { 73 | "type": "minecraft:pointed_dripstone", 74 | "config": { 75 | "chance_of_taller_dripstone": 0.2, 76 | "chance_of_directional_spread": 0.7, 77 | "chance_of_spread_radius2": 0.5, 78 | "chance_of_spread_radius3": 0.5 79 | } 80 | } 81 | ``` 82 | 83 | The placed feature is also located in the random selector: 84 | 85 | ```json title="placed_feature/pointed_dripstone.json" 86 | { 87 | "feature": "minecraft:pointed_dripstone", 88 | "placement": [ 89 | { 90 | "type": "minecraft:environment_scan", // (1)! 91 | "allowed_search_condition": { 92 | "type": "minecraft:matching_blocks", 93 | "blocks": [ 94 | "minecraft:air", 95 | "minecraft:water" 96 | ] 97 | }, 98 | "direction_of_search": "down", 99 | "max_steps": 12, 100 | "target_condition": { 101 | "type": "minecraft:solid" 102 | } 103 | }, 104 | { 105 | "type": "minecraft:random_offset", // (2)! 106 | "xz_spread": 0, 107 | "y_spread": 1 108 | } 109 | ] 110 | } 111 | ``` 112 | 113 | 1. Searches downwards for a solid block. 114 | 2. Makes sure to place the dripstone on top of the solid block. 115 | 116 | ## Result 117 | 118 | ![Example](https://i.imgur.com/DqCS6yo.png) -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/random-selector.md: -------------------------------------------------------------------------------- 1 | # Random selector feature 2 | 3 | The `random_selector` feature can be used to randomly choose from a provided list of features to place. 4 | 5 | ## Configuration 6 | 7 | The `random_selector` feature has the following configuration options: 8 | 9 | | Option | Type | Description | 10 | |------------|-----------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------| 11 | | `features` | A list of [placed features](../placed-feature.md) objects (or id's inside the `feature` option in Json) and their corresponding chance. | The list of features to choose from. | 12 | | `default` | A placed feature | The default feature to place if none of the provided features got picked | 13 | 14 | In code, the `RandomFeatureConfiguration` class is used to configure the feature. 15 | 16 | ## Example 17 | 18 | As an example, here's the random selector used to generate tree in the old growth taiga biome 19 | 20 | === "Kotlin" 21 | 22 | ```kotlin title="ConfiguredFeatures.kt" 23 | @OptIn(ExperimentalWorldGen::class) 24 | @Init(stage = InitStage.POST_PACK_PRE_WORLD)(stage = InitStage.POST_PACK_PRE_WORLD) 25 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 26 | 27 | val TREES_OLD_GROWTH_SPRUCE_TAIGA = registerConfiguredFeature( 28 | "trees_old_growth_spruce_taiga", 29 | Feature.RANDOM_SELECTOR, 30 | RandomFeatureConfiguration( 31 | listOf( 32 | WeightedPlacedFeature( 33 | VanillaRegistryAccess.getHolder(TreePlacements.MEGA_SPRUCE_CHECKED), // (1)! 34 | 1f / 3f // (2)! 35 | ), 36 | WeightedPlacedFeature( 37 | VanillaRegistryAccess.getHolder(TreePlacements.PINE_CHECKED), 38 | 1f / 3f // (3)! 39 | ) 40 | ), 41 | VanillaRegistryAccess.getHolder(TreePlacements.SPRUCE_CHECKED) // (4)! 42 | ) 43 | ) 44 | 45 | } 46 | ``` 47 | 48 | 1. Get the registry holder for the mega spruce tree placed feature. (Also support [inlined placed features](../placed-feature.md#inlined) 49 | 2. The chance of the mega spruce tree is $^1/_{3}$. 50 | 3. The chance of the pine tree is $^1/_{3}$. 51 | 4. The default tree is a spruce tree (with a $^1/_{3}$ chance). 52 | 53 | === "Json" 54 | 55 | ```json title="configured_feature/trees_old_growth_spruce_taiga.json" 56 | { 57 | "type": "minecraft:random_selector", 58 | "config": { 59 | "features": [ 60 | { // (1)! 61 | "chance": 0.33333334, 62 | "feature": "minecraft:mega_spruce_checked" // (2)! 63 | }, 64 | { // (3)! 65 | "chance": 0.33333334, 66 | "feature": "minecraft:pine_checked" 67 | } 68 | ], 69 | "default": "minecraft:spruce_checked" // (4)! 70 | } 71 | } 72 | ``` 73 | 74 | 1. A mega spruce tree with a $^1/_{3}$ chance. 75 | 2. As previously mentioned, this could also be an entire placed feature object. 76 | 3. A pine tree with a $^1/_{3}$ chance. 77 | 4. A spruce tree with a $^1/_{3}$ chance. 78 | 79 | ![Example](https://i.imgur.com/JJZoK77.jpeg) -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/replace-blobs.md: -------------------------------------------------------------------------------- 1 | # Replace blobs feature 2 | 3 | The `replace_blobs` feature can be used to replace specific blobs with a blob of a different block. Although 4 | the name suggests that it only works with netherrack, it can be used with any block. 5 | 6 | ## Configuration 7 | 8 | The `replace_blobs` feature has the following configuration options: 9 | 10 | | Option | Type | Description | 11 | |----------|---------------------------------------------------------------------------------------------------|---------------------------------------------| 12 | | `target` | A [`BlockState`](../../types/block-state.md). | The block state to replace. | 13 | | `state` | A `BlockState`. | The block state to replace the target with. | 14 | | `radius` | An [`IntProvider`](../../types/number-provider.md#intprovider). (Range limit in Json is $[0;12]$) | The radius of the blob. | 15 | 16 | In code, the `ReplaceSphereConfiguration` class is used to configure the feature. 17 | 18 | ## Example 19 | 20 | As an example, here's the configured- and placed feature for blackstone blobs in the nether. 21 | 22 | === "Kotlin" 23 | 24 | ```kotlin title="ConfiguredFeatures.kt" 25 | @OptIn(ExperimentalWorldGen::class) 26 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 27 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 28 | 29 | val BLACKSTONE_BLOBS = registerConfiguredFeature( 30 | "blackstone_blobs", 31 | Feature.REPLACE_BLOBS, 32 | ReplaceSphereConfiguration( 33 | Blocks.NETHERRACK.defaultBlockState(), // targetState 34 | Blocks.BLACKSTONE.defaultBlockState(), // replaceState 35 | UniformInt.of(3, 7) // radius // (1)! 36 | ) 37 | ) 38 | 39 | } 40 | ``` 41 | 42 | 1. Random radius between 3 and 7. 43 | 44 | ```kotlin title="PlacedFeatures.kt" 45 | @OptIn(ExperimentalWorldGen::class) 46 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 47 | object PlacedFeatures : FeatureRegistry by ExampleAddon.registry { 48 | 49 | val BLACKSTONE_BLOBS = placedFeature("blackstone_blobs", ConfiguredFeatures.BLACKSTONE_BLOBS) 50 | .count(25) // (1)! 51 | .inSquareSpread() // (2)! 52 | .modifier(PlacementUtils.FULL_RANGE) // (3)! 53 | .biomeFilter() // (4)! 54 | .register() 55 | 56 | } 57 | ``` 58 | 59 | 1. 25 tries to place the blobs per chunk. 60 | 2. Randomly spread the blobs horizontally. 61 | 3. Set the y-coordinate to a random value. The static constant is equivalent to 62 | ```kotlin 63 | HeightRangePlacement.uniform(VerticalAnchor.bottom(), VerticalAnchor.top()); 64 | ``` 65 | 4. Only place the blobs in biomes that have blackstone blobs. 66 | 67 | === "Json" 68 | 69 | ```json title="configured_feature/blackstone_blobs.json" 70 | { 71 | "type": "minecraft:replace_blobs", 72 | "config": { 73 | "target": { 74 | "Name": "minecraft:netherrack" 75 | }, 76 | "state": { 77 | "Name": "minecraft:blackstone" 78 | }, 79 | "radius": { 80 | "type": "minecraft:uniform", // (1)! 81 | "value": { 82 | "max_inclusive": 7, 83 | "min_inclusive": 3 84 | } 85 | } 86 | } 87 | } 88 | ``` 89 | 90 | 1. Random radius between 3 and 7. 91 | 92 | ```json title="placed_feature/blackstone_blobs.json" 93 | { 94 | "feature": "minecraft:blackstone_blobs", 95 | "placement": [ 96 | { 97 | "type": "minecraft:count", 98 | "count": 25 // (1)! 99 | }, 100 | { 101 | "type": "minecraft:in_square" // (2)! 102 | }, 103 | { 104 | "type": "minecraft:height_range", // (3)! 105 | "height": { 106 | "type": "minecraft:uniform", 107 | "max_inclusive": { 108 | "below_top": 0 109 | }, 110 | "min_inclusive": { 111 | "above_bottom": 0 112 | } 113 | } 114 | }, 115 | { 116 | "type": "minecraft:biome" // (4)! 117 | } 118 | ] 119 | } 120 | ``` 121 | 122 | 1. 25 tries to place the blobs per chunk. 123 | 2. Randomly spread the blobs horizontally. 124 | 3. Set the y-coordinate to a random value. 125 | 4. Only place the blobs in biomes that have blackstone blobs. 126 | 127 | ## Result 128 | 129 | ![Example](https://i.imgur.com/dVus6n5.gif) -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/replace-single-block.md: -------------------------------------------------------------------------------- 1 | # Replace single block feature 2 | 3 | The `replace_single_block` feature allows you to randomly replace blocks with another block. 4 | 5 | ## Configuration 6 | 7 | The `replace_single_block` feature has a single option, `targets` which similar to the `targets` option in the [ore feature](ores.md), 8 | allows you to specify which block states should be replaced with which other block states. Each `Target` has the following options: 9 | 10 | | Option | Type | Description | 11 | |----------|----------------------------------------------------------------------------------|----------------------------------------------------| 12 | | `target` | A `RuleTest`. Check out the [`ores`](ores.md#targets) page for more information. | The test to check if the block should be replaced. | 13 | | `state` | A [`BlockState`](../../types/block-state.md) | The block state to replace the target with. | 14 | 15 | In code, the `ReplaceBlockConfiguration` class is used to configure the feature. 16 | 17 | ## Example 18 | 19 | As an example, here's a feature to randomly replace acacia logs with stone. 20 | 21 | === "Kotlin" 22 | 23 | ```kotlin title="ConfiguredFeatures.kt" 24 | @OptIn(ExperimentalWorldGen::class) 25 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 26 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 27 | 28 | val ACACIA_LOGS_TO_STONE = registerConfiguredFeature( 29 | "acacia_logs_to_stone", 30 | Feature.REPLACE_SINGLE_BLOCK, 31 | ReplaceBlockConfiguration( 32 | listOf( 33 | OreConfiguration.target(TagMatchTest(BlockTags.ACACIA_LOGS), Blocks.STONE.defaultBlockState()) 34 | ) 35 | ) 36 | ) 37 | 38 | } 39 | ``` 40 | 41 | Or, if you only want to replace one specific block state 42 | 43 | ```kotlin title="ConfiguredFeatures.kt" 44 | @OptIn(ExperimentalWorldGen::class) 45 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 46 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 47 | 48 | val ACACIA_LOGS_TO_STONE = registerConfiguredFeature( 49 | "acacia_logs_to_stone", 50 | Feature.REPLACE_SINGLE_BLOCK, 51 | ReplaceBlockConfiguration( 52 | Blocks.ACACIA_LOG.defaultBlockState(), 53 | Blocks.STONE.defaultBlockState(), 54 | ) 55 | ) 56 | 57 | } 58 | ``` 59 | 60 | === "Json" 61 | 62 | ```json title="configured_feature/acacia_logs_to_stone.json" 63 | { 64 | "type": "minecraft:replace_single_block", 65 | "config": { 66 | "targets": [ 67 | { 68 | "target": { 69 | "predicate_type": "minecraft:tag_match", 70 | "tag": "minecraft:acacia_logs" 71 | }, 72 | "state": { 73 | "Name": "minecraft:stone" 74 | } 75 | } 76 | ] 77 | } 78 | } 79 | ``` 80 | -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/sea-pickle.md: -------------------------------------------------------------------------------- 1 | # Sea pickle feature 2 | 3 | As the name suggests, the `sea_pickle` feature generates sea pickles in the ocean. 4 | 5 | ## Configuration 6 | 7 | The `sea_pickle` feature only has a `count` [`IntProvider`](../../types/number-provider.md#intprovider) option. It determines the 8 | max amount of sea pickles per placement. 9 | 10 | In code, the `CountConfiguration` class is used to configure the feature. 11 | 12 | ## Example 13 | 14 | === "Kotlin" 15 | 16 | ```kotlin title="ConfiguredFeatures.kt" 17 | @OptIn(ExperimentalWorldGen::class) 18 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 19 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 20 | 21 | val SEA_PICKLE = registerConfiguredFeature( 22 | "sea_pickle", 23 | Feature.SEA_PICKLE, 24 | CountConfiguration(20) 25 | ) 26 | 27 | } 28 | ``` 29 | 30 | ```kotlin title="PlacedFeatures.kt" 31 | @OptIn(ExperimentalWorldGen::class) 32 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 33 | object PlacedFeatures : FeatureRegistry by ExampleAddon.registry { 34 | 35 | val SEA_PICKLE = placedFeature("sea_pickle", ConfiguredFeatures.SEA_PICKLE) 36 | .rarityFilter(16) // (1)! 37 | .inSquareSpread() // (2)! 38 | .moveToTopSolid() // (3)! 39 | .biomeFilter() // (4)! 40 | .register() 41 | 42 | } 43 | ``` 44 | 45 | 1. Place sea pickles in every 16th chunk. 46 | 2. Randomly offset the placement horizontally. 47 | 3. Set y-coordinate to the ocean floor. This call is equivalent to 48 | ```kotlin 49 | HeightmapPlacement.onHeightmap(Heightmap.Types.OCEAN_FLOOR_WG) 50 | ``` 51 | 4. Only place in the warm ocean biome. 52 | 53 | === "Json" 54 | 55 | ```json title="configured_feature/sea_pickle.json" 56 | { 57 | "type": "minecraft:sea_pickle", 58 | "config": { 59 | "count": 20 60 | } 61 | } 62 | ``` 63 | 64 | ```json title="placed_feature/sea_pickle.json" 65 | { 66 | "feature": "minecraft:sea_pickle", 67 | "placement": [ 68 | { 69 | "type": "minecraft:rarity_filter", 70 | "chance": 16 // (1)! 71 | }, 72 | { 73 | "type": "minecraft:in_square" // (2)! 74 | }, 75 | { 76 | "type": "minecraft:heightmap", 77 | "heightmap": "OCEAN_FLOOR_WG" // (3)! 78 | }, 79 | { 80 | "type": "minecraft:biome" // (4)! 81 | } 82 | ] 83 | } 84 | ``` 85 | 86 | 1. Place sea pickles in every 16th chunk. 87 | 2. Randomly offset the placement horizontally. 88 | 3. Set y-coordinate to the ocean floor. 89 | 4. Only place in the warm ocean biome. 90 | 91 | ![Example](https://i.imgur.com/0BTepnm.jpeg) -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/seagrass.md: -------------------------------------------------------------------------------- 1 | # Seagrass feature 2 | 3 | The `seagrass` feature allows you to generate seagrass in water. 4 | 5 | ## Configuration 6 | 7 | The `seagrass` feature only has the `probability` option (`float` in the range $[0.0;1.0)$), which determines the chance 8 | of seagrass being generated. 9 | 10 | In code, the `ProbabilityFeatureConfiguration` class is used to configure the feature. 11 | 12 | ## Example 13 | 14 | As an example, here's the configured- and placed feature of seagrass in the warm ocean biome. 15 | 16 | === "Kotlin" 17 | 18 | ```kotlin title="ConfiguredFeatures.kt" 19 | @OptIn(ExperimentalWorldGen::class) 20 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 21 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 22 | 23 | val SEAGRASS_TALL = registerConfiguredFeature( 24 | "seagrass_tall", 25 | Feature.SEAGRASS, 26 | ProbabilityFeatureConfiguration(0.3f) 27 | ) 28 | 29 | } 30 | ``` 31 | 32 | ```kotlin title="PlacedFeatures.kt" 33 | @OptIn(ExperimentalWorldGen::class) 34 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 35 | object PlacedFeatures : FeatureRegistry by ExampleAddon.registry { 36 | 37 | val SEAGRASS_TALL = placedFeature("seagrass_tall", ConfiguredFeatures.SEAGRASS_TALL) 38 | .inSquareSpread() // (1)! 39 | .moveToTopSolid() // (2)! 40 | .count(80) // (3)! 41 | .biomeFilter() // (4)! 42 | .register() 43 | 44 | } 45 | ``` 46 | 47 | 1. Randomly offset the placement horizontally. 48 | 2. Set y-coordinate to the ocean floor. This call is equivalent to 49 | ```kotlin 50 | HeightmapPlacement.onHeightmap(Heightmap.Types.OCEAN_FLOOR_WG) 51 | ``` 52 | 3. Place 80 seagrass per chunk. 53 | 4. Only place in the warm ocean biome. 54 | 55 | === "Json" 56 | 57 | ```json title="configured_feature/seagrass_tall.json" 58 | { 59 | "type": "minecraft:seagrass", 60 | "config": { 61 | "probability": 0.8 62 | } 63 | } 64 | ``` 65 | 66 | ```json title="placed_feature/seagrass_deep_warm.json" 67 | { 68 | "feature": "minecraft:seagrass_tall", 69 | "placement": [ 70 | { 71 | "type": "minecraft:in_square" // (1)! 72 | }, 73 | { 74 | "type": "minecraft:heightmap", 75 | "heightmap": "OCEAN_FLOOR_WG" // (2)! 76 | }, 77 | { 78 | "type": "minecraft:count", // (3)! 79 | "count": 80 80 | }, 81 | { 82 | "type": "minecraft:biome" // (4)! 83 | } 84 | ] 85 | } 86 | ``` 87 | 88 | 1. Randomly offset the placement horizontally. 89 | 2. Set y-coordinate to the ocean floor. 90 | 3. Place 80 seagrass per chunk. 91 | 4. Only place in the warm ocean biome. -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/simple-block.md: -------------------------------------------------------------------------------- 1 | # Simple block feature 2 | 3 | The `simple_block` feature allows you to randomly place blocks in the world. 4 | 5 | ## Configuration 6 | 7 | The `simple_block` feature has a single option, `to_place` which is a [`BlockStateProvider`](../../types/block-state-provider.md) 8 | that specifies which block states should be placed. 9 | 10 | In code, the `SimpleBlockConfiguration` class is used to configure the feature. 11 | 12 | ## Example 13 | 14 | As an example, here's the feature used to randomly place spore blossoms. 15 | 16 | === "Kotlin" 17 | 18 | ```kotlin title="ConfiguredFeatures.kt" 19 | @OptIn(ExperimentalWorldGen::class) 20 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 21 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 22 | 23 | val SPORE_BLOSSOM = registerConfiguredFeature( 24 | "spore_blossom", 25 | Feature.SIMPLE_BLOCK, 26 | SimpleBlockConfiguration(BlockStateProvider.simple(Blocks.SPORE_BLOSSOM)) 27 | ) 28 | 29 | } 30 | ``` 31 | 32 | ```kotlin title="PlacedFeatures.kt" 33 | @OptIn(ExperimentalWorldGen::class) 34 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 35 | object PlacedFeatures : FeatureRegistry by ExampleAddon.registry { 36 | 37 | val SPORE_BLOSSOM = placedFeature("spore_blossom", ConfiguredFeatures.SPORE_BLOSSOM) 38 | .count(25) // (1)! 39 | .inSquareSpread() // (2)! 40 | .inYWorldBounds() // (3)! 41 | .environmentScan(Direction.UP, BlockPredicate.solid(), BlockPredicate.ONLY_IN_AIR_PREDICATE, 12) // (4)! 42 | .randomVerticalOffset(-1) // (5)! 43 | .biomeFilter() // (6)! 44 | .register() 45 | 46 | } 47 | ``` 48 | 49 | 1. 25 attempts to place a spore blossom per chunk. 50 | 2. Randomly offset the location horizontally. 51 | 3. Set the y-coordinate to a random value up to 256. The call is equivalent to 52 | ```kotlin 53 | HeightRangePlacement.uniform(VerticalAnchor.bottom(), VerticalAnchor.absolute(256)); 54 | ``` 55 | 4. Search up to 12 blocks above the randomly selected location for a solid block. 56 | 5. Offset the location by -1 vertically. 57 | 6. Only place spore blossoms in biomes that have the `minecraft:spore_blossom` feature. 58 | 59 | === "Json" 60 | 61 | ```json title="configured_feature/spore_blossom.json" 62 | { 63 | "type": "minecraft:simple_block", 64 | "config": { 65 | "to_place": { 66 | "type": "minecraft:simple_state_provider", 67 | "state": { 68 | "Name": "minecraft:spore_blossom" 69 | } 70 | } 71 | } 72 | } 73 | ``` 74 | 75 | ```json title="placed_feature/spore_blossom.json" 76 | { 77 | "feature": "minecraft:spore_blossom", 78 | "placement": [ 79 | { 80 | "type": "minecraft:count", 81 | "count": 25 // (1)! 82 | }, 83 | { 84 | "type": "minecraft:in_square" // (2)! 85 | }, 86 | { 87 | "type": "minecraft:height_range", // (3)! 88 | "height": { 89 | "type": "minecraft:uniform", 90 | "max_inclusive": { 91 | "absolute": 256 92 | }, 93 | "min_inclusive": { 94 | "above_bottom": 0 95 | } 96 | } 97 | }, 98 | { 99 | "type": "minecraft:environment_scan", // (4)! 100 | "allowed_search_condition": { 101 | "type": "minecraft:matching_blocks", 102 | "blocks": "minecraft:air" 103 | }, 104 | "direction_of_search": "up", 105 | "max_steps": 12, 106 | "target_condition": { 107 | "type": "minecraft:solid" 108 | } 109 | }, 110 | { 111 | "type": "minecraft:random_offset", // (5)! 112 | "xz_spread": 0, 113 | "y_spread": -1 114 | }, 115 | { 116 | "type": "minecraft:biome" // (6)! 117 | } 118 | ] 119 | } 120 | ``` 121 | 122 | 1. 25 attempts to place a spore blossom per chunk. 123 | 2. Randomly offset the location horizontally. 124 | 3. Set the y-coordinate to a random value up to 256. 125 | 4. Search up to 12 blocks above the randomly selected location for a solid block. 126 | 5. Offset the location by -1 vertically. 127 | 6. Only place spore blossoms in biomes that have the `minecraft:spore_blossom` feature. 128 | 129 | ![Example](https://i.imgur.com/rFXdXU2.png) -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/tree.md: -------------------------------------------------------------------------------- 1 | TODO -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/features/configurations/twisting-vines.md: -------------------------------------------------------------------------------- 1 | # Twisting vines Feature 2 | 3 | !!! warning "Hardcoded block check" 4 | 5 | This feature has a hardcoded check for the block below the feature being `netherrack`, `warped_nylium` or `warped_wart_block` and will not work on other blocks 6 | 7 | The `twisting_vines` can be used to generate twisting vines in the world. 8 | 9 | ## Configuration 10 | 11 | The `twisting_vines` feature has the following configuration options: 12 | 13 | | Option | Type | Description | 14 | |-----------------|------------------|------------------------------------------------------------------------------------------| 15 | | `spread_width` | A positive `int` | Specifies the spread width of the twisting vines. Max width is `spread_width * 2 + 1` | 16 | | `spread_height` | A positive `int` | Specifies the spread height of the twisting vines. Max height is `spread_height * 2 + 1` | 17 | | `max_height` | A positive `int` | Specifies the maximum height of the twisting vines. Actual height is `max_height * 2` | 18 | 19 | In code, the `TwistingVinesConfig` class is used to configure the feature. 20 | 21 | ## Example 22 | 23 | As an example, here's the configured- and placed feature to generate twisting vines in the nether: 24 | 25 | === "Kotlin" 26 | 27 | ```kotlin title="ConfiguredFeatures.kt" 28 | @OptIn(ExperimentalWorldGen::class) 29 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 30 | object ConfiguredFeatures : FeatureRegistry by ExampleAddon.registry { 31 | 32 | val TWISTING_VINES = registerConfiguredFeature( 33 | "twisting_vines", 34 | Feature.TWISTING_VINES, 35 | TwistingVinesConfig( 36 | 8, // spreadWidth 37 | 4, // spreadHeight 38 | 8 // maxHeight 39 | ) 40 | ) 41 | 42 | } 43 | ``` 44 | 45 | ```kotlin title="PlacedFeatures.kt" 46 | @OptIn(ExperimentalWorldGen::class) 47 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 48 | object PlacedFeatures : FeatureRegistry by ExampleAddon.registry { 49 | 50 | val TWISTING_VINES = placedFeature("twisting_vines", ConfiguredFeatures.TWISTING_VINES) 51 | .count(10) // (1)! 52 | .inSquareSpread() // (2)! 53 | .modifier(PlacementUtils.FULL_RANGE) // (3)! 54 | .biomeFilter() // (4)! 55 | .register() 56 | 57 | } 58 | ``` 59 | 60 | 1. 10 twisting vines per chunk. 61 | 2. Spread the vines horizontally. 62 | 3. Set the y-coordinate to a random value. The static constant is equivalent to 63 | ```kotlin 64 | HeightRangePlacement.uniform(VerticalAnchor.bottom(), VerticalAnchor.top()); 65 | ``` 66 | 4. Only place the vines if the location hasn't moved outside the warped forest biome. 67 | 68 | === "Json" 69 | 70 | ```json title="configured_features/twisting_vines.json" 71 | { 72 | "type": "minecraft:twisting_vines", 73 | "config": { 74 | "spread_width": 8, 75 | "spread_height": 4, 76 | "max_height": 8 77 | } 78 | } 79 | ``` 80 | 81 | ```json title="placed_features/twisting_vines.json" 82 | { 83 | "feature": "minecraft:twisting_vines", 84 | "placement": [ 85 | { 86 | "type": "minecraft:count", // (1)! 87 | "count": 10 88 | }, 89 | { 90 | "type": "minecraft:in_square" // (2)! 91 | }, 92 | { 93 | "type": "minecraft:height_range", // (3)! 94 | "height": { 95 | "type": "minecraft:uniform", 96 | "max_inclusive": { 97 | "below_top": 0 98 | }, 99 | "min_inclusive": { 100 | "above_bottom": 0 101 | } 102 | } 103 | }, 104 | { 105 | "type": "minecraft:biome" // (4)! 106 | } 107 | ] 108 | } 109 | ``` 110 | 111 | 1. 10 twisting vines per chunk. 112 | 2. Spread the vines horizontally. 113 | 3. Set the y-coordinate to a random value. The static constant is equivalent to 114 | ```kotlin 115 | HeightRangePlacement.uniform(VerticalAnchor.bottom(), VerticalAnchor.top()); 116 | ``` 117 | 4. Only place the vines if the location hasn't moved outside the warped forest biome. 118 | 119 | ## Result 120 | 121 | === "Alone" 122 | 123 |

124 | Example 125 |

126 | 127 | === "Naturally generated" 128 | 129 |

130 | Example 131 |

-------------------------------------------------------------------------------- /docs/nova/addon/worldgen/inject/biome.md: -------------------------------------------------------------------------------- 1 | # Biome injections 2 | 3 | !!! warning 4 | 5 | This worldgen page is still a work in progress. Some Json formats/code examples/features might be missing and will be 6 | added in the future. 7 | 8 | `BiomeInjections` allow you to add [`PlacedFeatures`](../features/placed-feature.md) to an already existing biome. In the 9 | future, this will also allow you to modify any other biome property. 10 | 11 | ## Example usage 12 | 13 | Here's how you could inject the `star_shards_ore` `PlacedFeature` from Machines into all overworld biomes: 14 | 15 | === "Kotlin" 16 | 17 | ```kotlin title="BiomeInjections.kt" 18 | @OptIn(ExperimentalWorldGen::class) 19 | @Init(stage = InitStage.POST_PACK_PRE_WORLD) 20 | object BiomeInjections : BiomeRegistry by ExampleAddon.registry { 21 | 22 | private val OVERWORLD = biomeInjection("overworld") 23 | .biomes(BiomeTags.IS_OVERWORLD) 24 | .feature(Decoration.UNDERGROUND_ORES, PlacedFeatures.ORE_STAR_SHARDS) 25 | .register() 26 | 27 | } 28 | ``` 29 | 30 | === "Json" 31 | 32 | ```json title="data/worldgen/inject/biome/overworld.json" 33 | { 34 | "biomes": "#minecraft:is_overworld", 35 | "features": [ 36 | [], 37 | [], 38 | [], 39 | [], 40 | [], 41 | [], 42 | [ 43 | "machines:ore_star_shards" 44 | ], 45 | [], 46 | [], 47 | [], 48 | [] 49 | ] 50 | } 51 | ``` -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/types/block-predicate.md: -------------------------------------------------------------------------------- 1 | # BlockPredicates 2 | 3 | !!! warning 4 | 5 | This worldgen page is still a work in progress. Some Json formats/code examples/features might be missing and will be 6 | added in the future. 7 | 8 | TODO - Check out the [Minecraft Wiki](https://minecraft.wiki/w/Custom_world_generation/block_predicate) in the meantime. -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/types/block-state-provider.md: -------------------------------------------------------------------------------- 1 | # BlockState Providers 2 | 3 | !!! warning 4 | 5 | This worldgen page is still a work in progress. Some Json formats/code examples/features might be missing and will be 6 | added in the future. 7 | 8 | TODO - Check out the [Minecraft Wiki](https://minecraft.wiki/w/Custom_world_generation/block_state_provider) in the meantime. -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/types/block-state.md: -------------------------------------------------------------------------------- 1 | # BlockState 2 | 3 | !!! warning 4 | 5 | This worldgen page is still a work in progress. Some Json formats/code examples/features might be missing and will be 6 | added in the future. 7 | 8 | TODO - Check out the [Minecraft Wiki](https://minecraft.wiki/w/Custom_world_generation/block_state) in the meantime. -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/types/number-provider.md: -------------------------------------------------------------------------------- 1 | # NumberProviders 2 | 3 | !!! warning 4 | 5 | This worldgen page is still a work in progress. Some Json formats/code examples/features might be missing and will be 6 | added in the future. 7 | 8 | ## `IntProvider` 9 | 10 | TODO - Check out the [Minecraft Wiki](https://minecraft.wiki/w/Configured_feature/int_provider) in the meantime. 11 | 12 | ## `FloatProvider` 13 | 14 | TODO - Check out the [Minecraft Wiki](https://minecraft.wiki/w/Configured_feature/float_provider) in the meantime. -------------------------------------------------------------------------------- /docs/nova/addon/worldgen/worldgen.md: -------------------------------------------------------------------------------- 1 | # World Generation Overview 2 | 3 | Nova's worldgen is completely based on [Minecraft's custom worldgen format](https://minecraft.wiki/w/Custom_world_generation) with 4 | some additions. However, you can also register everything worldgen-related in code if you don't want to use Json. If you 5 | do decide to use Json files, make sure to create a `data/worldgen` directory in your addon's resources folder before you 6 | start. This is where all your worldgen files will be stored. 7 | 8 | Below you'll find a quick overview of the format how to use it. Check the sidebar for more detailed information. 9 | 10 | ### Structures 11 | 12 | Structures are used to generate structures or connected structures in the world (e.g. jungle temple, villages, etc.). 13 | **Currently not fully supported.** 14 | 15 | ### Features 16 | Check out the [`Features` Overview](features/features.md) page for more information. 17 | 18 | Features (sometimes also called decorators) are used to add additional decorations to the world (e.g. trees, ores, etc.). 19 | 20 | ### Carvers 21 | Check out the [`Carvers` Overview](carver.md) page for more information. 22 | 23 | Carvers are used to carve out caves and ravines in the world. 24 | 25 | ### Noise 26 | 27 | Noise setting are responsible for generating the terrain of worlds and determines which blocks to use. Because Nova's region 28 | file format is currently pretty inefficient, these settings aren't currently supported. 29 | 30 | ### Biomes 31 | Check out the [`Biomes`](biome.md) and [`BiomeInjections`](inject/biome.md) page for more information. 32 | 33 | Biomes are regions in the world with distinct [features](features/features.md), [carvers](carver.md), 34 | [climate](biome.md#climate), [effects](biome.md#special-effects) and much more. 35 | 36 | If you want to add `PlacedFeatures` to an already existing biome, check out the [`BiomeInjections`](inject/biome.md) page. 37 | 38 | ### Custom Dimensions 39 | 40 | **TODO** -------------------------------------------------------------------------------- /docs/nova/admin/compatibility/index.md: -------------------------------------------------------------------------------- 1 | ## Compatible server software 2 | 3 | Nova is a paper plugin and needs to be run on a paper server or paper fork. Officially supported are: 4 | 5 | * [x] [Paper](https://github.com/PaperMC/Paper) (recommended) 6 | * [x] [Purpur](https://github.com/PurpurMC/Purpur) 7 | * [x] [Pufferfish](https://github.com/pufferfish-gg/Pufferfish) 8 | 9 | Any other server software may or may not work properly with Nova. 10 | 11 | We're planning to add support for the following server software in the future: 12 | 13 | * [Folia](https://github.com/PaperMC/Folia) 14 | 15 | ## Compatibility with other plugins 16 | 17 | ### Custom Item Plugins 18 | 19 | - [ItemsAdder](itemsadder.md) 20 | - [Nexo](nexo.md) | [Known Issues](nexo#known-issues) 21 | - MMOItems 22 | 23 | !!! warning 24 | 25 | Most of these plugins require [resource pack merging](../setup.md#optional-resourcepack-merging). 26 | 27 | ### World Protection Plugins 28 | 29 | The following protection plugins are supported out of the box by Nova, but plugin developers may also 30 | add support on their end using [the API](../../api/protection/protectionintegration.md). 31 | 32 | - [x] WorldGuard 33 | - [x] GriefPrevention 34 | - [x] PlotSquared 35 | - [x] Towny 36 | - [x] ProtectionStones 37 | - [x] QuickShop 38 | - [x] Residence 39 | 40 | ## Incompatible Plugins 41 | 42 | The following plugins cannot be used with Nova: 43 | 44 | - [FastAsyncWorldEdit](https://www.spigotmc.org/resources/13932/) - Please consider using normal [WorldEdit](https://dev.bukkit.org/projects/worldedit) instead. -------------------------------------------------------------------------------- /docs/nova/admin/compatibility/itemsadder.md: -------------------------------------------------------------------------------- 1 | ## Adding compatibility with ItemsAdder 2 | 3 | To make ItemsAdder and Nova work together, you need to follow these steps: 4 | 5 | 1. Change the following values in ItemsAdder's `config.yml`: 6 | - Set `resource-pack` > `hosting` > `no-host` > `enabled` to `true` (all other options under `hosting` must be `false`). 7 | - Under `resource-pack` > `zip` > `protect-file-from-unzip` set both `protection_1` and `protection_2` to `false`. 8 | 2. Add the ItemsAdder resource pack zip file as a [base pack](../setup.md#optional-resourcepack-merging) in Nova's main config. 9 | 3. Regenerate Nova's resource pack with `/nova resourcePack create` (make sure that you've run `/iazip` before and the resource pack zip exists) 10 | 11 | ## Adding new assets to ItemsAdder 12 | 13 | After adding new assets to ItemsAdder and running `/iazip`, you will now also need to run `/nova resourcePack create`. -------------------------------------------------------------------------------- /docs/nova/admin/compatibility/nexo.md: -------------------------------------------------------------------------------- 1 | ## Adding compatibility with Nexo 2 | 3 | To make Nexo and Nova work together, you need to follow these steps: 4 | 5 | 1. Disable Nexo's resource pack upload in its `settings.yml` 6 | 2. Add the Nexo resource pack zip file as a [base pack](../setup.md#optional-resourcepack-merging) in Nova's main config. 7 | 3. Regenerate Nova's resource pack with `/nova resourcePack create` 8 | 9 | ## Known Issues 10 | 11 | * Duplicated and wrong block sounds -------------------------------------------------------------------------------- /docs/nova/admin/faq.md: -------------------------------------------------------------------------------- 1 | # FAQ 2 | 3 | ## Nova 4 | 5 | ??? faq "How do I get the resource pack?" 6 | 7 | Please follow the tutorial on the [setup page](setup.md). 8 | 9 | ??? faq "Which Minecraft versions are supported by Nova? Is there multi-version support?" 10 | 11 | The goal for Nova is to always target the latest version of Minecraft. There is no multi-version support. 12 | If the latest version of Nova does not support the latest Minecraft version yet, please be patient 13 | while we work on updating it. 14 | 15 | ??? faq "Can I use Nova with Bedrock bridges like Geyser?" 16 | 17 | No, Nova is not compatible with any Bedrock bridges. 18 | 19 | ??? faq "Where can I report bugs?" 20 | 21 | Please report all bugs [on GitHub](https://github.com/xenondevs/Nova/issues/new/choose). 22 | 23 | ??? faq "Where can I find the crafting recipes?" 24 | 25 | All items from Nova and their crafting recipes can be viewed by using `/nova items` and are also present in the 26 | vanilla crafting book (the green book in the crafting table). 27 | Additionally, recipes can also be viewed directly by using `/nvrecipe` and `/nvusage`. 28 | 29 | ??? faq "My items don't have textures / look like shulker shells." 30 | 31 | Please follow the tutorial on the [setup page](setup.md) and make sure that you've set up the resource pack properly. 32 | 33 | ??? faq "Is Nova compatible with Plugin X?" 34 | 35 | All compatible plugins and setup requirements are listed on the [compatibility page](compatibility/index.md). 36 | 37 | ## Official Addons 38 | 39 | ### Logistics 40 | 41 | ??? faq "How does the Item Filter work?" 42 | 43 | Right click in the air to set the items, then put the item filter in a cable or vacuum chest. 44 | 45 | ### Machines 46 | 47 | ??? faq "How do I get Star Shards?" 48 | 49 | You can find them in chests (like mineshafts, dungeons, desert temples...) or as star shards ore above ``y = 120``. -------------------------------------------------------------------------------- /docs/nova/admin/recipes/index.md: -------------------------------------------------------------------------------- 1 | # Customizing Recipes 2 | 3 | In Nova, all recipes are customizable. You can find the recipe files under ``plugins/Nova/recipes`` 4 | You can change or delete existing recipes or just create completely new ones. If a recipe gets updated by us, we will 5 | only update it on your server if you haven't modified or deleted it. 6 | 7 | You can reload all recipes by running the command `/nova reload recipes`. 8 | 9 | ## Item Formats 10 | 11 | There are multiple ways for you to specify an item in a recipe: 12 | 13 | ### Custom Item Format 14 | 15 | This format allows you to use a custom item from Nova or any other supported plugin. For example ``itemsadder:ruby`` 16 | would also work here. 17 | 18 | ```json title="Custom Item Format" 19 | "machines:copper_gear" 20 | ``` 21 | 22 | With items from Nova addons, it is also possible to use the ``nova:`` prefix instead of the addon's id. This will cause all items from different addons with that id to be accepted. For example, if multiple addons define a ``copper_dust``, ``machines:copper_dust`` would only accept the copper dust from the Machines addon, but ``nova:copper_dust`` would allow any copper dust. 23 | 24 | ### Complex Item Format 25 | 26 | This format lets you fully customize the required item. If you use this format, you won't be able to use custom 27 | namespaces like ``nova:`` 28 | As this checks the item exactly (only ignoring the item name), you might encounter some issues with enchantments and 29 | other data stored inside the item. 30 | This is the same format as in Minecraft's /give command. As it is in JSON, quotes need to be escaped. 31 | 32 | ```json title="Complex Item Format" 33 | "minecraft:potion{\"Potion\": \"minecraft:water\"}" 34 | ``` 35 | 36 | ## Item- & Recipe Fallbacks 37 | 38 | While this is more intended for developers, item- and recipe fallbacks can also be used by server administrators. 39 | 40 | **What are item- and recipe fallbacks?** 41 | Fallbacks can be used to define an item or recipe to fall back to when the item could not be found or the recipe could not be loaded. 42 | This is useful for addon developers as it allows them to use items from other addons in their crafting recipes without creating a hard dependency on that addon. 43 | 44 | ### Item Fallbacks 45 | 46 | Item fallbacks are defined by adding a semicolon after the item declaration, followed by a second declaration: 47 | ```json title="Item Fallback" 48 | "nova:basic_fluid_tank; minecraft:bucket" 49 | ``` 50 | The recipe loader will first check if ``nova:basic_fluid_tank`` exists. If not, ``minecraft:bucket`` is used. 51 | 52 | ### Recipe Fallbacks 53 | 54 | In some cases, it makes sense to completely change the structure of a recipe if the items from another addon are missing. 55 | For that, just put multiple recipe objects into a json array: 56 | 57 | ??? example "Example Recipes" 58 | 59 | === "Normal" 60 | 61 | ```json title="recipe.json" 62 | [ 63 | { 64 | "result": "addon1:result_item", 65 | "shape": [ 66 | "a ", 67 | " a ", 68 | " a" 69 | ], 70 | "ingredients": { 71 | "a": "addon2:example_item" 72 | } 73 | }, 74 | { 75 | "result": "addon1:result_item", 76 | "shape": [ 77 | " a ", 78 | " a ", 79 | " a " 80 | ], 81 | "ingredients": { 82 | "a": "addon1:fallback_item" 83 | } 84 | } 85 | ] 86 | ``` 87 | 88 | !!! info 89 | 90 | If required, it is also possible to use item fallbacks inside of recipe fallbacks. 91 | 92 | === "With failSilently" 93 | 94 | If you don't want any exceptions in the console if none of the fallbacks could be loaded, you can set the ``failSilently`` boolean to ``true``. 95 | 96 | ```json title="recipe.json" 97 | { 98 | "failSilently": true, 99 | "recipes": [ 100 | { 101 | "result": "addon1:result_item", 102 | "shape": [ 103 | "a ", 104 | " a ", 105 | " a" 106 | ], 107 | "ingredients": { 108 | "a": "addon2:example_item" 109 | } 110 | }, 111 | { 112 | "result": "addon1:result_item", 113 | "shape": [ 114 | " a ", 115 | " a ", 116 | " a " 117 | ], 118 | "ingredients": { 119 | "a": "addon1:fallback_item" 120 | } 121 | } 122 | ] 123 | } 124 | ``` 125 | 126 | !!! info 127 | 128 | If required, it is also possible to use item fallbacks inside of recipe fallbacks. -------------------------------------------------------------------------------- /docs/nova/admin/recipes/machines.md: -------------------------------------------------------------------------------- 1 | # Recipe Types from the Machines Addon 2 | 3 | ## Pulverizer Recipes 4 | 5 | This is an example recipe for pulverizing iron ore into iron dust: 6 | 7 | ```json title="iron_ore_to_iron_dust.json" 8 | { 9 | "input": [ 10 | "minecraft:iron_ore", 11 | "minecraft:deepslate_iron_ore" 12 | ], 13 | "result": "machines:iron_dust", 14 | "amount": 2, 15 | "time": 200 16 | } 17 | ``` 18 | 19 | ## Mechanical Press Recipes 20 | 21 | Recipes for the mechanical press are grouped in the sub-folders ``gear/`` and ``plate/`` 22 | Both use the same syntax. 23 | This is an example recipe for pressing an iron plate: 24 | 25 | ```json title="iron_ingot_to_iron_plate.json" 26 | { 27 | "input": "minecraft:iron_ingot", 28 | "result": "machines:iron_plate", 29 | "time": 200 30 | } 31 | ``` 32 | 33 | !!! info "Multiple Choices for Ingredients" 34 | 35 | If you want to give players multiple item choices for a single ingredient, you can specify an array of strings instead of a single string for the input. 36 | 37 | ## Fluid Infuser Recipes 38 | 39 | The Fluid Infuser can be used in two modes: Inserting fluids into an item or extract fluids from an item. 40 | This is an example recipe for inserting water into a bucket to create a water bucket: 41 | 42 | ```json title="bucket_to_water_bucket.json" 43 | { 44 | "mode": "INSERT", // (1)! 45 | "fluid_type": "WATER", // (2)! 46 | "fluid_amount": 1000, 47 | "input": "minecraft:bucket", 48 | "result": "minecraft:water_bucket", 49 | "time": 100 50 | } 51 | ``` 52 | 53 | 1. The mode specifies if this should be a recipe for inserting (``INSERT``) or extracting (``EXTRACT``) fluids. 54 | 2. Currently, there are only ``WATER`` and ``LAVA``. Custom fluids from other plugins are not supported. 55 | 56 | This is an example recipe for extract water from a water bottle to create an empty bottle: 57 | 58 | ```json title="potion_to_glass_bottle.json" 59 | { 60 | "mode": "EXTRACT", 61 | "fluid_type": "WATER", 62 | "fluid_amount": 300, 63 | "input": "minecraft:potion{\"Potion\": \"minecraft:water\"}", 64 | "result": "minecraft:glass_bottle", 65 | "time": 30 66 | } 67 | ``` 68 | 69 | !!! info "Custom Item Format" 70 | 71 | As a water bottle is not its own item, a [Complex Item Format](index.md#complex-item-format) is required here. 72 | 73 | ## Electric Brewing Stand Recipes 74 | 75 | Using these recipes, you can configure which potion types can be created using the electric brewing stand. You're also 76 | able to configure their ingredients, as well as multipliers for duration and amplifier level and the max amount of these 77 | levels. 78 | 79 | This recipe would add the luck effect type to the electric brewing stand: 80 | 81 | ```json title="luck.json" 82 | { 83 | "result": "minecraft:luck", 84 | "default_time": 1800, // (1)! 85 | "redstone_multiplier": 2, // (2)! 86 | "glowstone_multiplier": 0.5, // (3)! 87 | "max_duration_level": 5, // (4)! 88 | "max_amplifier_level": 5, // (5)! 89 | "inputs": [ 90 | "minecraft:nether_wart", 91 | "minecraft:grass_block" 92 | ] 93 | } 94 | ``` 95 | 96 | 1. The default time a potion with this effect would have. In ticks, 20 ticks = 1s. This potion would have a default time of 1:30 97 | 2. The time multiplier when a duration level (one redstone) is added. This means a luck potion with level two duration would last 3:00, level three 4:30 and so on. 98 | 3. In Minecraft, when glowstone is added to a potion in order to increase the amplifier level, the duration is reduced. This is represented by this multiplier. A potion with an amplifier of level two would have a duration of 0:45, level three 0:11 and so on. 99 | 4. The maximum allowed duration level for a potion of this effect. By default, you cannot create an effect with both an increased duration level and an increased amplifier level, but you are able to change this in the config file for the electric brewing stand. 100 | 5. The maximum allowed amplifier level for a potion of this effect. By default, you cannot create an effect with both an increased duration level and an increased amplifier level, but you are able to change this in the config file for the electric brewing stand. 101 | 6. In this recipe type, multiple item choices for one ingredient are **not** allowed. 102 | -------------------------------------------------------------------------------- /docs/nova/admin/recipes/vanilla.md: -------------------------------------------------------------------------------- 1 | # Vanilla Recipe Types 2 | 3 | ## Shaped Recipes 4 | 5 | Directory: `shaped` 6 | 7 | Shaped recipes are recipes for the Crafting Table that have to follow a specific structure. This is an example crafting 8 | recipe for the Advanced Cable: 9 | 10 | ```json title="advanced_cable.json" 11 | { 12 | "result": "logistics:advanced_cable", 13 | "amount": 3, // (2)! 14 | "shape": [ // (1)! 15 | "ggg", 16 | "ccc", 17 | "ggg" 18 | ], 19 | "ingredients": { 20 | "g": "minecraft:glowstone_dust", 21 | "c": "logistics:basic_cable" 22 | } 23 | } 24 | ``` 25 | 26 | 1. The shape is the structure of the recipe. You can see that it resembles a crafting table. The letters you use here have to also be present in the ``ingredients`` section. You can use a space if that slot should be empty. 27 | 2. The amount of items that will be crafted, can be omitted for 1. 28 | 29 | !!! info "Multiple Choices for Ingredients" 30 | 31 | If you want to give players multiple item choices for a single ingredient, you can specify an array of strings instead of a single string for each ingredient. 32 | 33 | ## Shapeless Recipes 34 | 35 | Directory: `shapeless` 36 | 37 | Shapeless recipes are recipes for the Crafting Table that do not follow a specific structure. This is an example recipe 38 | for the Basic Item Filter: 39 | 40 | ```json title="basic_item_filter.json" 41 | { 42 | "result": "logistics:basic_item_filter", 43 | "ingredients": { 44 | "minecraft:hopper": 1, // (1)! 45 | "minecraft:paper": 1 46 | } 47 | } 48 | ``` 49 | 50 | 1. This number represents the amount of items that needs to be present in the crafting table. 51 | 52 | Like in shaped recipes, it is also possible to give multiple choices for a specific ingredient. 53 | The following recipe would allow players to either use paper or an iron plate to craft a Basic Item Filter. 54 | 55 | ```json title="basic_item_filter.json" 56 | { 57 | "result": "logistics:basic_item_filter", 58 | "ingredients": [ 59 | { 60 | "item": "minecraft:hopper", 61 | "amount": 1 62 | }, 63 | { 64 | "items": [ 65 | "nova:iron_plate", 66 | "minecraft:paper" 67 | ], 68 | "amount": 1 69 | } 70 | ] 71 | } 72 | ``` 73 | 74 | ## Furnace Recipes 75 | 76 | Directories: `furance`, `blast_furnace`, `smoker`, `campfire` 77 | 78 | This is an example recipe for smelting iron dust into iron ingots: 79 | 80 | ```json title="iron_dust_to_iron_ingot.json" 81 | { 82 | "result": "minecraft:iron_ingot", 83 | "input": "machines:iron_dust", 84 | "experience": 1.0, 85 | "time": 100 // (1)! 86 | } 87 | ``` 88 | 89 | 1. The time is in ticks. One second is 20 ticks. 90 | 91 | !!! info "Multiple Choices for Ingredients" 92 | 93 | If you want to give players multiple item choices for a single ingredient, you can specify an array of strings instead of a single string for the input. 94 | 95 | ## Stonecutter Recipes 96 | 97 | Directory: `stonecutter` 98 | 99 | This is an example recipe for the Stonecutter that allows players to craft three stone slabs from a stone block: 100 | 101 | ```json title="stone_to_slab.json" 102 | { 103 | "result": "minecraft:stone_slab", 104 | "input": "minecraft:stone", 105 | "amount": 3 106 | } 107 | ``` 108 | 109 | !!! info "Multiple Choices for Ingredients" 110 | 111 | If you want to give players multiple item choices for a single ingredient, you can specify an array of strings instead of a single string for the input. 112 | 113 | ## Smithing Transform Recipes 114 | 115 | Directory: `smithing_transform` 116 | 117 | This is an example recipe for upgrading a diamond hammer to a netherite hammer: 118 | 119 | ```json title="diamond_to_netherite_helmet.json" 120 | { 121 | "template": "minecraft:netherite_upgrade_smithing_template", 122 | "base": "vanilla_hammers:diamond_hammer", 123 | "addition": "minecraft:netherite_ingot", 124 | "result": "vanilla_hammers:netherite_hammer" 125 | } 126 | ``` 127 | 128 | !!! info "Multiple Choices for Ingredients" 129 | 130 | If you want to give players multiple item choices for a single ingredient, you can specify an array of strings instead of a single string for the input. 131 | -------------------------------------------------------------------------------- /docs/nova/api/blocks/blockregistry.md: -------------------------------------------------------------------------------- 1 | # Block Registry 2 | 3 | The block registry contains all registered block types. 4 | 5 | You can get the ``BlockRegistry`` using [the previously retrieved Nova instance](../index.md). 6 | 7 | === "Kotlin" 8 | 9 | ```kotlin 10 | val blockRegistry = Nova.blockRegistry 11 | ``` 12 | 13 | === "Java" 14 | 15 | ```java 16 | BlockRegistry blockRegistry = Nova.getNova().getBlockRegistry(); 17 | ``` 18 | 19 | After that, you can retrieve a block type by its id: 20 | 21 | === "Kotlin" 22 | 23 | ```kotlin 24 | val block = blockRegistry.getBlock("machines:pulverizer") 25 | ``` 26 | 27 | === "Java" 28 | 29 | ```java 30 | NovaBlock block = blockRegistry.getBlock("machines:pulverizer"); 31 | ``` -------------------------------------------------------------------------------- /docs/nova/api/events/novaloaddataevent.md: -------------------------------------------------------------------------------- 1 | # NovaLoadDataEvent 2 | 3 | The ``NovaLoadDataEvent`` is called when Nova finished initializing on server startup/reload. -------------------------------------------------------------------------------- /docs/nova/api/events/tileentitybreakblockevent.md: -------------------------------------------------------------------------------- 1 | # TileEntityBreakBlockEvent 2 | 3 | The ``TileEntityBreakBlockEvent`` is called when a tile-entity breaks a block. It can be used to manipulate the drops of 4 | the broken block. 5 | 6 | !!! warning 7 | 8 | This event can't be cancelled. Please see [ProtectionIntegration](../protection/protectionintegration.md). 9 | 10 | ## Properties 11 | 12 | ### tileEntity 13 | 14 | The ``TileEntity`` that broke the block. 15 | 16 | ### block 17 | 18 | The ``Block`` that was broken. 19 | 20 | ### drops 21 | 22 | A ``MutableList`` of ``ItemStacks`` that will be added to the tile-entities inventory (or dropped on the ground if the 23 | inventory is full). This list can be modified to change the drops. 24 | 25 | ## Examples 26 | 27 | ### Flint from dirt 28 | 29 | Adding a 25% chance to get 1 flint when breaking a dirt block with a tile-entity. 30 | 31 | === "Kotlin" 32 | 33 | ```kotlin 34 | @EventHandler 35 | fun handleBlockBreak(event: TileEntityBreakBlockEvent) { 36 | if (event.block.type == Material.DIRT 37 | && Random.nextInt(0, 100) <= 25 // 25% chance 38 | ) { 39 | event.drops.add(ItemStack(Material.FLINT, 1)) // Add a flint to the drops 40 | } 41 | } 42 | ``` 43 | 44 | === "Java" 45 | 46 | ```java 47 | @EventHandler 48 | public void handleBlockBreak(TileEntityBreakBlockEvent event) { 49 | if (event.getBlock().getType() == Material.DIRT 50 | && random.nextInt(100) <= 25 // 25% chance 51 | ) { 52 | List drops = event.getDrops(); 53 | drops.add(new ItemStack(Material.FLINT, 1)); // Add a flint to the drops 54 | event.setDrops(drops); 55 | } 56 | } 57 | ``` -------------------------------------------------------------------------------- /docs/nova/api/index.md: -------------------------------------------------------------------------------- 1 | To use the Nova API you first have to add the xenondevs maven repository to your build configuration. 2 | 3 | === "Maven" 4 | 5 | ```xml 6 | 7 | xenondevs 8 | https://repo.xenondevs.xyz/releases 9 | 10 | ``` 11 | 12 | === "Gradle Groovy" 13 | 14 | ```groovy 15 | maven { 16 | url 'https://repo.xenondevs.xyz/releases' 17 | } 18 | ``` 19 | 20 | === "Gradle Kotlin" 21 | 22 | ```kotlin 23 | maven("https://repo.xenondevs.xyz/releases") 24 | ``` 25 | 26 | Now you can add the API dependency to your build configuration: 27 | 28 | === "Maven" 29 | 30 | ```xml 31 | 32 | xyz.xenondevs.nova 33 | nova-api 34 | VERSION 35 | provided 36 | 37 | ``` 38 | 39 | === "Gradle Groovy" 40 | 41 | ```groovy 42 | implementation "xyz.xenondevs.nova:nova-api:VERSION" 43 | ``` 44 | 45 | === "Gradle Kotlin" 46 | 47 | ```kotlin 48 | implementation("xyz.xenondevs.nova:nova-api:VERSION") 49 | ``` 50 | 51 | To get the Nova instance you can use the `Nova` class: 52 | 53 | === "Kotlin" 54 | 55 | ```kotlin 56 | val nova = Nova // (1)! 57 | ``` 58 | 59 | 1. `Nova` is an interface but the companion object delegates to ``Bukkit.getPluginManager().getPlugin("Nova") as Nova``. 60 | 61 | === "Java" 62 | 63 | ```java 64 | Nova nova = Nova.getNova(); 65 | ``` 66 | 67 | You can use this instance to access everything else: 68 | 69 | - [Adding custom protection checks](./protection/protectionintegration.md) 70 | - [Getting `NovaItems`](./items/index.md) 71 | - [Getting `NovaBlocks`](./blocks/blockregistry.md) 72 | - [Working with `NovaBlocks` and `NovaBlockStates`](./blocks/blockmanager.md) 73 | - [Working with `TileEntities`](./tileentity/tileentitymanager.md) 74 | - [Toggling the WAILA overlay](./player/wailamanager.md) -------------------------------------------------------------------------------- /docs/nova/api/items/index.md: -------------------------------------------------------------------------------- 1 | ``NovaItem`` represents an item type. It is similar to ``Material`` in Bukkit, except that it is only for items. 2 | 3 | ## Getting a ``NovaItem`` by name 4 | 5 | To get a ``NovaItem`` you first have to get the ``NovaItemRegistry`` using [the previously retrieved Nova instance](../index.md). 6 | 7 | === "Kotlin" 8 | 9 | ```kotlin 10 | val itemRegistry = Nova.itemRegistry 11 | ``` 12 | 13 | === "Java" 14 | 15 | ```java 16 | NovaItemRegistry itemRegistry = nova.getItemRegistry(); // (1)! 17 | ``` 18 | 19 | 1. "nova" is the previously retrieved Nova instance, preferably saved in a field/variable.
You can also call ``Nova.getNova().getItemRegistry()`` 20 | 21 | Using this registry, you can now get a ``NovaItem`` by id. It needs to be in the format ``namespace:name``. 22 | 23 | === "Kotlin" 24 | 25 | ```kotlin 26 | val item = itemRegistry.get("nova:wrench") // (1)! 27 | ``` 28 | 29 | 1. This will throw an exception if the item is not found. However, wrench always exists.
If you're unsure or processing user input use ``getOrNull`` instead. 30 | 31 | === "Java" 32 | 33 | ```java 34 | NovaItem item = itemRegistry.get("nova:wrench"); // (1)! 35 | ``` 36 | 37 | 1. This will throw an exception if the item is not found. However, wrench always exists.
If you're unsure or processing user input use ``getOrNull`` instead. 38 | 39 | 40 | !!! info 41 | 42 | The same methods also exist for getting a ``NovaItem`` from an ``ItemStack``. 43 | 44 | If you want to retrieve all items with a certain name and ignore the namespace, you can do this: 45 | 46 | === "Kotlin" 47 | 48 | ```kotlin 49 | val items = itemRegistry.getNonNamespaced("wrench") 50 | ``` 51 | 52 | === "Java" 53 | 54 | ```java 55 | List items = itemRegistry.getNonNamespaced("wrench"); 56 | ``` 57 | 58 | ## Getting the id of an item 59 | 60 | Example for ``nova:wrench``: 61 | 62 | === "Kotlin" 63 | 64 | ```kotlin 65 | val id = item.id 66 | 67 | val namespace = id.namespace // "nova" 68 | val name = id.name // "wrench" 69 | val idString = id.toString() // "nova:wrench" 70 | ``` 71 | 72 | === "Java" 73 | 74 | ```java 75 | NamespacedId id = item.getId(); 76 | 77 | String namespace = id.getNamespace(); // "nova" 78 | String name = id.getName(); // "wrench" 79 | String idString = id.toString(); // "nova:wrench" 80 | ``` 81 | 82 | ## Getting the translated name of an item 83 | 84 | Nova uses the resource pack to translate items client side. However, if you still need to get the translated name of an item, 85 | you can use ``NovaItem.getLocalizedName(locale)``. The locale is the language code of the language you want to get the name in. 86 | The code is the same as the language code used by [Minecraft](https://minecraft.wiki/w/Language). 87 | 88 | !!! info 89 | 90 | If the given language code could not be found or is invalid, the english name of the item will be returned. 91 | 92 | Example for ``nova:wrench``: 93 | 94 | === "Kotlin" 95 | 96 | ```kotlin 97 | val name = item.getLocalizedName("de_de") // "Schraubenschlüssel" 98 | ``` 99 | 100 | === "Java" 101 | 102 | ```java 103 | String name = item.getLocalizedName("de_de"); // "Schraubenschlüssel" 104 | ``` -------------------------------------------------------------------------------- /docs/nova/api/player/wailamanager.md: -------------------------------------------------------------------------------- 1 | # WailaManager 2 | 3 | The WailaManager allows you to enable / disable the WAILA overlay for players. 4 | 5 | === "Kotlin" 6 | 7 | ```kotlin 8 | // Get the current state 9 | val enabled = wailaManager.getState(player) 10 | 11 | // Enable the WAILA overlay 12 | wailaManager.setState(player, true) 13 | // Disable the WAILA overlay 14 | wailaManager.setState(player, false) 15 | ``` 16 | 17 | === "Java" 18 | 19 | ```java 20 | // Get the current state 21 | boolean enabled = wailaManager.getState(player); 22 | 23 | // Enable the WAILA overlay 24 | wailaManager.setState(player, true); 25 | // Disable the WAILA overlay 26 | wailaManager.setState(player, false); 27 | ``` -------------------------------------------------------------------------------- /docs/nova/api/protection/protectionintegration.md: -------------------------------------------------------------------------------- 1 | # ProtectionIntegration 2 | 3 | If you want to add a custom protection integration for your own plugin, you can do so by implementing the 4 | ``ProtectionIntegration`` interface. This interface provides a handful of methods that will be called by the 5 | ``ProtectionManager``. 6 | 7 | For an example of how to implement this interface, you can check out some of our 8 | [built-in protection integrations](https://github.com/xenondevs/Nova/tree/main/nova-hooks). 9 | 10 | Once you have implemented the interface, you can register it with ``Nova.registerProtectionIntegration(ProtectionIntegration)``. 11 | -------------------------------------------------------------------------------- /docs/nova/api/tileentity/tileentity.md: -------------------------------------------------------------------------------- 1 | # TileEntity 2 | 3 | A TileEntity is a block that has internal logic and tick-based updates. 4 | 5 | ## Getting the owner of a TileEntity 6 | 7 | === "Kotlin" 8 | 9 | ```kotlin 10 | val tileEntity = tileEntityManager.getTileEntityAt(location) ?: return 11 | val owner = tileEntity.owner 12 | ``` 13 | 14 | === "Java" 15 | 16 | ```java 17 | TileEntity tileEntity = tileEntityManager.getTileEntityAt(location); 18 | if (tileEntity == null) 19 | return; 20 | OfflinePlayer owner = tileEntity.getOwner(); 21 | ``` 22 | 23 | ## Getting the localized name of a TileEntity 24 | 25 | Using the [`NovaBlock`](../blocks/blockregistry.md) of a TileEntity, you can get the name of a TileEntity. 26 | 27 | For this example, we'll get the english name of a Pulverizer. 28 | 29 | === "Kotlin" 30 | 31 | ```kotlin 32 | val tileEntity = tileEntityManager.getTileEntityAt(location) ?: return 33 | val name = tileEntity.block.getLocalizedName("en_us") 34 | println(name) // prints "Pulverizer" 35 | ``` 36 | 37 | === "Java" 38 | 39 | ```java 40 | TileEntity tileEntity = tileEntityManager.getTileEntityAt(location); 41 | if(tileEntity == null) 42 | return; 43 | String name = tileEntity.getMaterial().getLocalizedName("en_us"); 44 | System.out.println(name); // prints "Pulverizer" 45 | ``` 46 | 47 | ## Getting the drops of a TileEntity 48 | 49 | These drops include all items in the TileEntity's inventory and the TileEntity itself if ``includeSelf`` is set to ``true``. 50 | 51 | === "Kotlin" 52 | 53 | ```kotlin 54 | val tileEntity = tileEntityManager.getTileEntityAt(location) ?: return 55 | val drops = tileEntity.getDrops(includeSelf = true) 56 | ``` 57 | 58 | === "Java" 59 | 60 | ```java 61 | TileEntity tileEntity = tileEntityManager.getTileEntityAt(location); 62 | if (tileEntity == null) 63 | return; 64 | List drops = tileEntity.getDrops(true); 65 | ``` 66 | 67 | For more information about the TileEntityManager, see the [TileEntityManager](../tileentity/tileentitymanager.md) page. -------------------------------------------------------------------------------- /docs/nova/api/tileentity/tileentitymanager.md: -------------------------------------------------------------------------------- 1 | # TileEntityManager 2 | 3 | You can get the ``TileEntityManager`` using [the previously retrieved Nova instance](../index.md). 4 | 5 | === "Kotlin" 6 | 7 | ```kotlin 8 | val tileEntityManager = Nova.tileEntityManager 9 | ``` 10 | 11 | === "Java" 12 | 13 | ```java 14 | TileEntityManager tileEntityManager = nova.getTileEntityManager(); // (1)! 15 | ``` 16 | 17 | 1. "nova" is the previously retrieved Nova instance, preferably saved in a field/variable.
You can also call ``Nova.getNova().getTileEntityManager()`` 18 | 19 | ## Getting a TileEntity at a specific location 20 | 21 | Getting a TileEntity at a specific location is done by calling the ``getTileEntityAt`` function of the ``TileEntityManager``. 22 | The function either returns a TileEntity or ``null`` if there is no TileEntity at the specified location. 23 | 24 | === "Kotlin" 25 | 26 | ```kotlin 27 | val tileEntity: TileEntity? = tileEntityManager.getTileEntityAt(location) 28 | ``` 29 | 30 | === "Java" 31 | 32 | ```java 33 | TileEntity tileEntity = tileEntityManager.getTileEntityAt(location); 34 | ``` -------------------------------------------------------------------------------- /docs/nova/assets/js/mathjax.js: -------------------------------------------------------------------------------- 1 | window.MathJax = { 2 | tex: { 3 | inlineMath: [["\\(", "\\)"]], 4 | displayMath: [["\\[", "\\]"]], 5 | processEscapes: true, 6 | processEnvironments: true 7 | }, 8 | options: { 9 | ignoreHtmlClass: ".*|", 10 | processHtmlClass: "arithmatex", 11 | enableMenu: false 12 | } 13 | }; 14 | 15 | document$.subscribe(() => { 16 | MathJax.typesetPromise() 17 | }) -------------------------------------------------------------------------------- /docs/nova/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xenondevs/Docs/59ad882f0f1c9eb9dc27ce136a68b70dfde63db7/docs/nova/assets/logo.png -------------------------------------------------------------------------------- /docs/nova/assets/stylesheets/extra.css: -------------------------------------------------------------------------------- 1 | /*noinspection CssUnresolvedCustomProperty*/ 2 | :root > * { 3 | --md-primary-fg-color: #1B1D1F; 4 | --md-primary-bg-color: #FFFFFF; 5 | --md-default-bg-color: #151719; 6 | --md-typeset-a-color: var(--md-code-hl-function-color); 7 | --md-default-fg-color--light: #abb9cb; 8 | 9 | --md-code-hl-color: hsla(163, 34%, 67%, 0.5); 10 | --md-code-hl-number-color: #0aa370; 11 | --md-code-hl-special-color: #73a0d3; 12 | --md-code-hl-function-color: #7eb6f6; 13 | --md-code-hl-constant-color: #abb9cb; 14 | --md-code-hl-keyword-color: #47ebb4; 15 | --md-code-hl-string-color: #7eb6f6; 16 | --md-code-hl-name-color: var(--md-code-fg-color); 17 | --md-code-hl-operator-color: #47ebb4; 18 | --md-code-hl-punctuation-color: #abb9cb; 19 | --md-code-hl-comment-color: #abb9cb; 20 | --md-code-hl-generic-color: var(--md-default-fg-color--light); 21 | --md-code-hl-variable-color: #7eb6f6; 22 | } 23 | 24 | @media only screen and (min-width: 76.25em) { 25 | .md-nav--lifted>.md-nav__list>.md-nav__item--active>.md-nav__link:not(.md-nav__container) { 26 | width: 90%; 27 | } 28 | 29 | .md-main__inner { 30 | max-width: none; 31 | } 32 | 33 | .md-content { 34 | max-width: 56.5%; 35 | } 36 | 37 | .md-sidebar--primary { 38 | margin-left: 3.75%; 39 | margin-right: 3.75%; 40 | } 41 | 42 | .md-sidebar--secondary { 43 | margin-left: 3.75%; 44 | margin-right: 3.75%; 45 | -webkit-transform: none; 46 | transform: none; 47 | } 48 | 49 | body::-webkit-scrollbar { 50 | width: 15px; 51 | height: 15px; 52 | } 53 | 54 | body::-webkit-scrollbar-thumb { 55 | background-color: #21222c; 56 | border-radius: 15px; 57 | } 58 | 59 | body::-webkit-scrollbar-track { 60 | background-color: #2e303e; 61 | } 62 | 63 | body { 64 | scrollbar-color: #21222c #2e303e; 65 | } 66 | 67 | } 68 | 69 | .md-nav { 70 | font-size: 14px; 71 | line-height: 1.4; 72 | } 73 | 74 | .md-nav__link { 75 | display: inline-flex; 76 | } 77 | 78 | .md-typeset { 79 | font-size: .7rem; 80 | line-height: 1.5; 81 | } 82 | 83 | .md-typeset :not(pre) > code { 84 | background-color: #23262b; 85 | } 86 | 87 | .text-center { 88 | text-align: center !important; 89 | } 90 | 91 | .md-tabs { 92 | box-shadow: rgba(0, 0, 0, 0.1) 0px 0px 0.2rem, rgba(0, 0, 0, 0.2) 0px 0.2rem 0.4rem; 93 | transition: transform 0.25s cubic-bezier(0.1, 0.7, 0.1, 1) 0s, box-shadow 0.25s ease 0s; 94 | } 95 | 96 | .md-nav__item .md-nav__link--active { 97 | color: #768390; 98 | } 99 | 100 | .md-typeset .admonition { 101 | background-color: var(--md-primary-fg-color); 102 | border-width: 0; 103 | border-left-width: 4px; 104 | } 105 | 106 | .md-typeset details { 107 | background-color: var(--md-primary-fg-color); 108 | border-width: 0; 109 | border-left-width: 4px; 110 | } 111 | 112 | .md-typeset table:not([class]) { 113 | border-width: 0; 114 | box-shadow: 0 0.2rem 0.5rem hsla(0, 0%, 0%, 0.3),0 0 0.05rem hsla(0, 0%, 0%, 0.2); 115 | border-radius: 5px; 116 | } 117 | 118 | .md-typeset th { 119 | background-color: #111111; 120 | } 121 | 122 | .md-typeset td { 123 | background-color: #1b1d1f; 124 | } 125 | 126 | .md-typeset tbody tr:first-child td { 127 | border-color: #3593ff; 128 | } 129 | 130 | .md-footer { 131 | background-color: var(--md-primary-fg-color); 132 | } 133 | 134 | .md-footer-meta { 135 | background: none; 136 | } 137 | 138 | .md-typeset .md-button { 139 | color: #fff; 140 | } 141 | 142 | .md-typeset .md-button:hover { 143 | background-color: rgba(255, 255, 255, 0.1); 144 | border-color: rgba(255, 255, 255, 0); 145 | } -------------------------------------------------------------------------------- /docs/nova/index.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | ![](https://i.imgur.com/X4Q2ycT.png) --------------------------------------------------------------------------------