mirror of
https://github.com/ViaVersion/mcstructs-viafier.git
synced 2024-11-15 10:25:16 +01:00
Init
This commit is contained in:
commit
433c526e03
66
.github/workflows/main.yml
vendored
Normal file
66
.github/workflows/main.yml
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
name: Publish viafied mcstructs
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
run:
|
||||
if: ${{ github.event_name != 'pull_request' || github.repository != github.event.pull_request.head.repo.full_name }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Init submodule
|
||||
run: git submodule update --init
|
||||
|
||||
- name: Check for changes
|
||||
id: check_changes
|
||||
run: |
|
||||
git fetch origin && [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/HEAD)" ] && echo "changes=y" >> $GITHUB_OUTPUT || true
|
||||
working-directory: MCStructs
|
||||
|
||||
- name: Setup Python
|
||||
if: steps.check_changes.outputs.changes == 'y'
|
||||
uses: actions/setup-python@v4
|
||||
|
||||
- name: Update and patch mcstructs
|
||||
if: steps.check_changes.outputs.changes == 'y'
|
||||
run: |
|
||||
git submodule update --remote
|
||||
python mcstructs.py
|
||||
|
||||
- name: Setup Java JDK
|
||||
if: steps.check_changes.outputs.changes == 'y'
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
check-latest: true
|
||||
|
||||
- name: Publish
|
||||
if: steps.check_changes.outputs.changes == 'y'
|
||||
env:
|
||||
via_username: ${{ secrets.VIA_USERNAME }}
|
||||
via_password: ${{ secrets.VIA_PASSWORD }}
|
||||
run: ./gradlew publish
|
||||
working-directory: MCStructs
|
||||
|
||||
- name: Commit changes
|
||||
if: steps.check_changes.outputs.changes == 'y'
|
||||
run: |
|
||||
git config --local user.email "action@github.com"
|
||||
git config --local user.name "automation"
|
||||
git add .
|
||||
git commit -am "Automated update"
|
||||
|
||||
- name: Push changes
|
||||
if: steps.check_changes.outputs.changes == 'y'
|
||||
uses: ad-m/github-push-action@master
|
||||
with:
|
||||
github_token: ${{ secrets.TOKEN }}
|
||||
branch: ${{ github.ref }}
|
94
.gitignore
vendored
Normal file
94
.gitignore
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
venv/
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
!gradle/wrapper/
|
||||
|
||||
### Intellij ###
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
|
||||
# Ignore project files
|
||||
*.iml
|
||||
|
||||
# Ignore IDEA directory
|
||||
.idea/*
|
||||
!.idea/copyright/*
|
||||
!.idea/scopes/*
|
||||
|
||||
# Include the project's dictionary
|
||||
!.idea/dictionaries/
|
||||
!.idea/dictionaries/*
|
||||
|
||||
# File-based project format:
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
### Plugin-specific files: ###
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
|
||||
|
||||
|
||||
### Eclipse ###
|
||||
*.pydevproject
|
||||
.metadata
|
||||
.gradle
|
||||
bin/
|
||||
tmp/
|
||||
*.tmp
|
||||
*.bak
|
||||
*.swp
|
||||
*~.nib
|
||||
local.properties
|
||||
.settings/
|
||||
.loadpath
|
||||
|
||||
# Eclipse Core
|
||||
.project
|
||||
|
||||
# External tool builders
|
||||
.externalToolBuilders/
|
||||
|
||||
# Locally stored "Eclipse launch configurations"
|
||||
*.launch
|
||||
|
||||
# CDT-specific
|
||||
.cproject
|
||||
|
||||
# JDT-specific (Eclipse Java Development Tools)
|
||||
.classpath
|
||||
|
||||
# PDT-specific
|
||||
.buildpath
|
||||
|
||||
# sbteclipse plugin
|
||||
.target
|
||||
|
||||
# TeXlipse plugin
|
||||
.texlipse
|
||||
|
||||
|
||||
|
||||
### NetBeans ###
|
||||
nbproject/private/
|
||||
build/
|
||||
nbbuild/
|
||||
dist/
|
||||
nbdist/
|
||||
nbactions.xml
|
||||
nb-configuration.xml
|
||||
.nb-gradle/
|
||||
|
||||
### MacOS ###
|
||||
.DS_Store
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "MCStructs"]
|
||||
path = MCStructs
|
||||
url = https://github.com/Lenni0451/MCStructs.git
|
1
MCStructs
Submodule
1
MCStructs
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 190e8eac84324a777f8283d378f8957ba0472ded
|
13
README.md
Normal file
13
README.md
Normal file
@ -0,0 +1,13 @@
|
||||
Replaces mcstruct's inbuilt nbt library with ViaNBT.
|
||||
|
||||
## Setup
|
||||
```bash
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
Then you can run the Python script to apply the automated updates.
|
||||
```bash
|
||||
py mcstructs.py
|
||||
cd MCStructs
|
||||
gradlew test
|
||||
```
|
185
mcstructs.py
Normal file
185
mcstructs.py
Normal file
@ -0,0 +1,185 @@
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
via_nbt_version = '3.4.0'
|
||||
|
||||
replacements = {
|
||||
# Gradle build script changes (less chance of running into conflicts by putting it here instead of the patch)
|
||||
'name = "lenni0451"': 'name = "viaversion"',
|
||||
'url = "https://maven.lenni0451.net/everything"': 'url = "https://repo.viaversion.com/"',
|
||||
'api project(":MCStructs-nbt")': f'api "com.viaversion:nbt:{via_nbt_version}"',
|
||||
'maven_group=net.lenni0451.mcstructs': 'maven_group=com.viaversion.mcstructs',
|
||||
# Code changes
|
||||
'import net.lenni0451.mcstructs.nbt.tags.': 'import com.github.steveice10.opennbt.tag.builtin.',
|
||||
'import net.lenni0451.mcstructs.nbt.': 'import com.github.steveice10.opennbt.tag.builtin.',
|
||||
'INbtTag': 'Tag',
|
||||
'INbtNumber': 'NumberTag',
|
||||
'ListTag<>': 'ListTag',
|
||||
'ListTag<Tag>': 'ListTag',
|
||||
'ListTag<CompoundTag>': 'ListTag',
|
||||
'ListTag<T>': 'ListTag',
|
||||
'ListTag<?>': 'ListTag',
|
||||
'tag.getNbtType()': 'tag',
|
||||
'ArrayTag.getLength()': 'ArrayTag.length()',
|
||||
'ArrayTag.get(': 'ArrayTag.getValue(',
|
||||
'type.name()': 'type.getClass().getSimpleName()',
|
||||
'NbtType': 'Tag',
|
||||
'import com.github.steveice10.opennbt.tag.builtin.Tag': 'import com.github.steveice10.opennbt.tag.builtin.*',
|
||||
'import com.github.steveice10.opennbt.tag.builtin.CompoundTag': 'import com.github.steveice10.opennbt.tag.builtin.*',
|
||||
# A special one
|
||||
' if (!list.canAdd(tag)) throw new SNbtDeserializeException("Unable to insert " + tag.getClass().getSimpleName() + " into ListTag of type " + list.getType().name());': ''
|
||||
}
|
||||
|
||||
# Apply these separately to avoid changing json calls
|
||||
extra_replacements = {
|
||||
'.addString(': '.putString(',
|
||||
'.addBoolean(': '.putBoolean(',
|
||||
'.addByte(': '.putByte(',
|
||||
'.addLong(': '.putLong(',
|
||||
'.addInt(': '.putInt(',
|
||||
'.addDouble(': '.putDouble(',
|
||||
'.addShort(': '.putShort(',
|
||||
'.addCompound(': '.put(',
|
||||
'.addAll(': '.putAll(',
|
||||
'.getTag().name()': '.getClass()',
|
||||
'tag.name()': 'tag.getClass()',
|
||||
'list.getType().isNumber()': 'list.getElementType().isAssignableFrom(com.github.steveice10.opennbt.tag.builtin.NumberTag.class)',
|
||||
'this.styleSerializer.serialize(object.getStyle()).asCompoundTag()': '(CompoundTag) this.styleSerializer.serialize(object.getStyle())',
|
||||
'compound.add(': 'compound.put(',
|
||||
'.numberValue()': '.getValue()',
|
||||
'(ACTION)': '("action")',
|
||||
'(CONTENTS, ': '("contents", ',
|
||||
'(CONTENTS)': '("contents")',
|
||||
'tags.get(0).getTag()': 'tags.get(0)',
|
||||
# Special ones
|
||||
'!type.equals(tags.get(i).getTag())': "type.getClass() != tags.get(i).getClass()",
|
||||
'String type = ((StringTag) tag.get("type")).getValue()': 'String type = tag.contains("type") ? ((StringTag) tag.get("type")).getValue() : null'
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
os.chdir('MCStructs')
|
||||
|
||||
handle_file('build.gradle')
|
||||
handle_file('gradle.properties')
|
||||
deep('MCStructs-snbt')
|
||||
deep('MCStructs-text')
|
||||
|
||||
# Apply additional manual changes
|
||||
apply_patch('../patch.patch')
|
||||
|
||||
|
||||
def apply_patch(patch_file):
|
||||
try:
|
||||
subprocess.run(['git', 'apply', '--reject', '--ignore-whitespace', '--ignore-space-change', patch_file],
|
||||
check=True)
|
||||
print('Applied patch')
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f'Error applying patch: {e}')
|
||||
|
||||
|
||||
def deep(path):
|
||||
for p in os.listdir(path):
|
||||
p = os.path.join(path, p)
|
||||
if os.path.isdir(p):
|
||||
deep(p)
|
||||
elif p.endswith(".java") or p.endswith(".gradle"):
|
||||
handle_file(p)
|
||||
|
||||
|
||||
def replace_get_value(content, obj):
|
||||
# First apply nullable replacements with the generic replacement, THEN the rest
|
||||
content = replace_nullable_get(obj, 'Compound', '', 'null', content)
|
||||
content = replace_nullable_get(obj, 'List', '', 'null', content)
|
||||
content = re.sub(fr'{obj}\.get(\w+)\(([^,)]+), null\)',
|
||||
fr'({obj}.get(\2) instanceof \1Tag ? ((\1Tag) {obj}.get(\2)).getValue() : null)',
|
||||
content)
|
||||
|
||||
content = replace_nonnull_get(obj, 'Compound', '', 'new CompoundTag()', content)
|
||||
content = replace_nonnull_get(obj, 'List', '', 'new ListTag()', content)
|
||||
content = replace_nonnull_get(obj, 'String', '.getValue()', '""', content)
|
||||
content = replace_nonnull_get(obj, 'ByteArray', '.getValue()', 'new byte[0]', content)
|
||||
content = replace_nonnull_get(obj, 'IntArray', '.getValue()', 'new int[0]', content)
|
||||
content = replace_nonnull_get(obj, 'LongArray', '.getValue()', 'new long[0]', content)
|
||||
|
||||
# Booleans are stored as byte tags
|
||||
content = re.sub(r'tag\.getBoolean\(([^)]+)\)',
|
||||
fr'({obj}.get(\1) instanceof ByteTag ? ((ByteTag) {obj}.get(\1)).asBoolean() : false)',
|
||||
content)
|
||||
|
||||
numeric_types = {'Byte', 'Short', 'Int', 'Long', 'Float', 'Double'}
|
||||
for numeric_type in numeric_types:
|
||||
content = replace_nonnull_get(obj, numeric_type, f'.as{numeric_type}()', '0', content)
|
||||
|
||||
return content
|
||||
|
||||
|
||||
def replace_nullable_get(obj, tag_type, value_method, default, content):
|
||||
pattern = re.compile(fr'{obj}\.get{tag_type}\(([^,)]+), null\)')
|
||||
replacement = fr'({obj}.get(\1) instanceof {tag_type}Tag ? (({tag_type}Tag) {obj}.get(\1)){value_method} : {default})'
|
||||
return pattern.sub(replacement, content)
|
||||
|
||||
|
||||
def replace_nonnull_get(obj, tag_type, value_method, default, content):
|
||||
pattern = re.compile(fr'{obj}\.get{tag_type}\(([^)]+)\)')
|
||||
replacement = fr'({obj}.get(\1) instanceof {tag_type}Tag ? (({tag_type}Tag) {obj}.get(\1)){value_method} : {default})'
|
||||
return pattern.sub(replacement, content)
|
||||
|
||||
|
||||
def handle_file(path):
|
||||
with open(path, 'r') as file:
|
||||
content = file.read()
|
||||
|
||||
changed_content = content
|
||||
for old, new in replacements.items():
|
||||
changed_content = changed_content.replace(old, new)
|
||||
|
||||
name = os.path.basename(path)
|
||||
if 'CompoundTag ' in changed_content:
|
||||
# Some are very explicit, so that they don't break random code elsewhere/make it look like it worked fine
|
||||
for old, new in extra_replacements.items():
|
||||
changed_content = changed_content.replace(old, new)
|
||||
|
||||
if 'JsonHoverEventSerializer' not in name:
|
||||
changed_content = changed_content.replace('.add("', '.put("')
|
||||
|
||||
# tag.isXTag() -> (tag instanceof XTag)
|
||||
changed_content = re.sub(r'(\w+)\.is(\w+Tag)\(\)', r'(\1 instanceof \2)', changed_content)
|
||||
|
||||
# asXTag() -> cast
|
||||
changed_content = re.sub(r'(\w+)\.as(\w+Tag)\(\)', r'((\2) \1)', changed_content)
|
||||
changed_content = re.sub(r'tags.get\(i\)\.as(\w+Tag)\(\)', r'((\1) tags.get(i))', changed_content)
|
||||
|
||||
# tag.contains(s, Tag.X) -> tag.get(s) instanceof XTag
|
||||
changed_content = re.sub(r'(\w+)\.contains\(([^,)]+), Tag\.(\w+)\)',
|
||||
lambda m: f'({m.group(1)}.get({m.group(2)}) instanceof {m.group(3).capitalize()}Tag)',
|
||||
changed_content)
|
||||
changed_content = re.sub(r'\.contains\(([^,)]+), Tag\.(\w+)\)',
|
||||
lambda m: f'.get({m.group(1)}) instanceof {m.group(2).capitalize()}Tag',
|
||||
changed_content)
|
||||
|
||||
# Tag.X.equals(tagType) -> tagType instance XTag
|
||||
changed_content = re.sub(r'Tag\.(\w+).equals\((\w+)\)',
|
||||
lambda m: f'({m.group(2)} instanceof {m.group(1).capitalize()}Tag)',
|
||||
changed_content)
|
||||
|
||||
# tag.getX -> cast to tag with default value
|
||||
changed_content = replace_get_value(changed_content, 'tag')
|
||||
changed_content = replace_get_value(changed_content, 'rawTag')
|
||||
changed_content = replace_get_value(changed_content, 'rawEntity')
|
||||
changed_content = replace_get_value(changed_content, 'score')
|
||||
changed_content = replace_get_value(changed_content, 'clickEvent')
|
||||
changed_content = replace_get_value(changed_content, 'parsed')
|
||||
|
||||
if content == changed_content:
|
||||
return
|
||||
|
||||
with open(path, 'w') as file:
|
||||
file.write(changed_content)
|
||||
|
||||
print('Wrote to', path)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
126
patch.patch
Normal file
126
patch.patch
Normal file
@ -0,0 +1,126 @@
|
||||
diff --git a/MCStructs-snbt/src/main/java/net/lenni0451/mcstructs/snbt/impl/v1_12/SNbtDeserializer_v1_12.java b/MCStructs-snbt/src/main/java/net/lenni0451/mcstructs/snbt/impl/v1_12/SNbtDeserializer_v1_12.java
|
||||
index 8bec86c..3525056 100644
|
||||
--- a/MCStructs-snbt/src/main/java/net/lenni0451/mcstructs/snbt/impl/v1_12/SNbtDeserializer_v1_12.java
|
||||
+++ b/MCStructs-snbt/src/main/java/net/lenni0451/mcstructs/snbt/impl/v1_12/SNbtDeserializer_v1_12.java
|
||||
@@ -95,10 +95,28 @@ public class SNbtDeserializer_v1_12 implements ISNbtDeserializer<CompoundTag> {
|
||||
reader.read();
|
||||
reader.skipWhitespaces();
|
||||
if (!reader.canRead()) throw this.makeException(reader, "Expected value");
|
||||
- else if (c == 'B') return new ByteArrayTag(this.readPrimitiveList(reader, ByteTag.class, ByteArrayTag.class));
|
||||
- else if (c == 'L') return new LongArrayTag(this.readPrimitiveList(reader, LongTag.class, LongArrayTag.class));
|
||||
- else if (c == 'I') return new IntArrayTag(this.readPrimitiveList(reader, IntTag.class, IntArrayTag.class));
|
||||
- else throw new SNbtDeserializeException("Invalid array type '" + c + "' found");
|
||||
+ else if (c == 'B') {
|
||||
+ final ListTag tags = this.readPrimitiveList(reader, ByteTag.class, ByteArrayTag.class);
|
||||
+ final byte[] array = new byte[tags.size()];
|
||||
+ for (int i = 0; i < tags.size(); i++) {
|
||||
+ array[i] = tags.get(i).value();
|
||||
+ }
|
||||
+ return new ByteArrayTag(array);
|
||||
+ } else if (c == 'L') {
|
||||
+ final ListTag tags = this.readPrimitiveList(reader, LongTag.class, LongArrayTag.class);
|
||||
+ final long[] array = new long[tags.size()];
|
||||
+ for (int i = 0; i < tags.size(); i++) {
|
||||
+ array[i] = tags.get(i).value();
|
||||
+ }
|
||||
+ return new LongArrayTag(array);
|
||||
+ } else if (c == 'I') {
|
||||
+ final ListTag tags = this.readPrimitiveList(reader, IntTag.class, IntArrayTag.class);
|
||||
+ final int[] array = new int[tags.size()];
|
||||
+ for (int i = 0; i < tags.size(); i++) {
|
||||
+ array[i] = tags.get(i).value();
|
||||
+ }
|
||||
+ return new IntArrayTag(array);
|
||||
+ } else throw new SNbtDeserializeException("Invalid array type '" + c + "' found");
|
||||
}
|
||||
|
||||
protected Tag readValue(final StringReader_v1_12 reader) throws SNbtDeserializeException {
|
||||
diff --git a/MCStructs-text/src/test/java/net/lenni0451/mcstructs/text/serializer/TextComponentCodecTest.java b/MCStructs-text/src/test/java/net/lenni0451/mcstructs/text/serializer/TextComponentCodecTest.java
|
||||
index 86e32ec..9eb184c 100644
|
||||
--- a/MCStructs-text/src/test/java/net/lenni0451/mcstructs/text/serializer/TextComponentCodecTest.java
|
||||
+++ b/MCStructs-text/src/test/java/net/lenni0451/mcstructs/text/serializer/TextComponentCodecTest.java
|
||||
@@ -60,9 +60,9 @@ class TextComponentCodecTest {
|
||||
|
||||
@Test
|
||||
void legacyItemDeserialization() throws SNbtSerializeException {
|
||||
- CompoundTag legacyNbt = new CompoundTag()
|
||||
- .put("id", "stone")
|
||||
- .putByte("Count", (byte) 5);
|
||||
+ CompoundTag legacyNbt = new CompoundTag();
|
||||
+ legacyNbt.put("id", new StringTag("stone"));
|
||||
+ legacyNbt.putByte("Count", (byte) 5);
|
||||
ATextComponent legacyComponent = new StringComponent("test")
|
||||
.setStyle(new Style()
|
||||
.setHoverEvent(new TextHoverEvent(HoverEventAction.SHOW_ITEM, new StringComponent(SNbtSerializer.LATEST.serialize(legacyNbt))))
|
||||
@@ -79,10 +79,10 @@ class TextComponentCodecTest {
|
||||
@Test
|
||||
void legacyEntityDeserialization() throws SNbtSerializeException {
|
||||
UUID randomUUID = UUID.randomUUID();
|
||||
- CompoundTag legacyNbt = new CompoundTag()
|
||||
- .put("name", "{\"text\":\"test\"}")
|
||||
- .put("type", "cow")
|
||||
- .put("id", randomUUID.toString());
|
||||
+ CompoundTag legacyNbt = new CompoundTag();
|
||||
+ legacyNbt.put("name", new StringTag("{\"text\":\"test\"}"));
|
||||
+ legacyNbt.put("type", new StringTag("cow"));
|
||||
+ legacyNbt.put("id", new StringTag(randomUUID.toString()));
|
||||
ATextComponent legacyComponent = new StringComponent("test")
|
||||
.setStyle(new Style()
|
||||
.setHoverEvent(new TextHoverEvent(HoverEventAction.SHOW_ENTITY, new StringComponent(SNbtSerializer.LATEST.serialize(legacyNbt))))
|
||||
diff --git a/build.gradle b/build.gradle
|
||||
index a8c4682..6929dc4 100644
|
||||
--- a/build.gradle
|
||||
+++ b/build.gradle
|
||||
@@ -48,17 +48,17 @@ subprojects {
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
- name = "reposilite"
|
||||
- def releasesUrl = "https://maven.lenni0451.net/releases"
|
||||
- def snapshotsUrl = "https://maven.lenni0451.net/snapshots"
|
||||
- url = project.maven_version.endsWith("SNAPSHOT") ? snapshotsUrl : releasesUrl
|
||||
-
|
||||
- credentials(PasswordCredentials)
|
||||
+ name = "Via"
|
||||
+ url = uri("https://repo.viaversion.com/")
|
||||
+ credentials {
|
||||
+ username = System.getenv("via_username")
|
||||
+ password = System.getenv("via_password")
|
||||
+ }
|
||||
authentication {
|
||||
- basic(BasicAuthentication)
|
||||
+ create(BasicAuthentication)
|
||||
}
|
||||
}
|
||||
- maven {
|
||||
+ /*maven {
|
||||
name = "ossrh"
|
||||
def releasesUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
|
||||
def snapshotsUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
|
||||
@@ -68,7 +68,7 @@ subprojects {
|
||||
authentication {
|
||||
basic(BasicAuthentication)
|
||||
}
|
||||
- }
|
||||
+ }*/
|
||||
}
|
||||
publications {
|
||||
maven(MavenPublication) {
|
||||
diff --git a/settings.gradle b/settings.gradle
|
||||
index 678ff0b..dd6c9cc 100644
|
||||
--- a/settings.gradle
|
||||
+++ b/settings.gradle
|
||||
@@ -7,13 +7,6 @@ pluginManagement {
|
||||
|
||||
rootProject.name = "MCStructs"
|
||||
|
||||
-include(":MCStructs-all")
|
||||
-include(":MCStructs-data")
|
||||
-include(":MCStructs-enchantments")
|
||||
include(":MCStructs-core")
|
||||
-include(":MCStructs-inventory")
|
||||
-include(":MCStructs-items")
|
||||
-include(":MCStructs-nbt")
|
||||
-include(":MCStructs-recipes")
|
||||
include(":MCStructs-snbt")
|
||||
include(":MCStructs-text")
|
Loading…
Reference in New Issue
Block a user