This commit is contained in:
Nassim Jahnke 2023-12-28 12:20:38 +01:00
commit 433c526e03
No known key found for this signature in database
GPG Key ID: EF6771C01F6EF02F
7 changed files with 488 additions and 0 deletions

66
.github/workflows/main.yml vendored Normal file
View 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
View 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
View File

@ -0,0 +1,3 @@
[submodule "MCStructs"]
path = MCStructs
url = https://github.com/Lenni0451/MCStructs.git

1
MCStructs Submodule

@ -0,0 +1 @@
Subproject commit 190e8eac84324a777f8283d378f8957ba0472ded

13
README.md Normal file
View 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
View 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
View 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")