From 4b8a39e1b29063af5f974005de36bf0c6ef1c0bb Mon Sep 17 00:00:00 2001 From: Capsup Date: Wed, 4 Mar 2026 21:52:21 +0100 Subject: [PATCH 1/4] fix: manifest version and labels --- .gitignore | 52 ++++ build.gradle | 277 ++++++++++++++++++ settings.gradle | 2 +- .../heightmap/HyghtmapModPlugin.java | 44 ++- .../UI/Custom/Pages/HeightmapImportPage.ui | 46 +-- .../Server/Languages/en-US/hyghtmap.lang | 40 +++ .../en-US/net.wolvesfortress.heightmap.lang | 33 --- src/main/resources/manifest.json | 3 +- 8 files changed, 438 insertions(+), 59 deletions(-) create mode 100644 .gitignore create mode 100644 src/main/resources/Server/Languages/en-US/hyghtmap.lang delete mode 100644 src/main/resources/Server/Languages/en-US/net.wolvesfortress.heightmap.lang diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ff2fc6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/*.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store + +### Gradle ### +.gradle +.kotlin +devserver +/.idea/ + +### HyghtmapMod ### +# Downloaded server JAR (large, copyrighted) +libs/ +# Generated API reference +.hytale-api/ \ No newline at end of file diff --git a/build.gradle b/build.gradle index 17d715d..2cde67c 100644 --- a/build.gradle +++ b/build.gradle @@ -66,4 +66,281 @@ tasks.register('watch') { println "Watching for changes... Press Ctrl+C to stop." println "Run 'gradle build --continuous' for auto-rebuild on file changes." } +} + +// ── API Reference Generation ──────────────────────────────────────────────── +// Run: ./gradlew apiRef +// Downloads HytaleServer.jar and generates plain-text API reference files +// in .hytale-api/ for quick lookup during development. + +configurations { + apiDump +} + +dependencies { + apiDump 'org.ow2.asm:asm:9.7.1' + apiDump 'org.ow2.asm:asm-tree:9.7.1' +} + +tasks.register('dumpServerApi') { + description = 'Generates text-based API reference from libs/HytaleServer.jar' + group = 'api-reference' + + def serverJar = file('libs/HytaleServer.jar') + def outputDir = file('.hytale-api') + + inputs.file(serverJar) + outputs.dir(outputDir) + + doLast { + // Load ASM via the apiDump configuration + def asmUrls = configurations.apiDump.files.collect { it.toURI().toURL() } as URL[] + def asmLoader = new URLClassLoader(asmUrls, getClass().classLoader) + + def ClassReader = asmLoader.loadClass('org.objectweb.asm.ClassReader') + def ClassNode = asmLoader.loadClass('org.objectweb.asm.tree.ClassNode') + + // ── Helper closures ───────────────────────────────────────────── + + def descriptorToType = { String desc -> + int arrayDim = 0 + def d = desc + while (d.startsWith('[')) { arrayDim++; d = d.substring(1) } + def base + switch (d) { + case 'V': base = 'void'; break + case 'Z': base = 'boolean'; break + case 'B': base = 'byte'; break + case 'C': base = 'char'; break + case 'S': base = 'short'; break + case 'I': base = 'int'; break + case 'J': base = 'long'; break + case 'F': base = 'float'; break + case 'D': base = 'double'; break + default: + if (d.startsWith('L') && d.endsWith(';')) { + base = d.substring(1, d.length() - 1).replace('/', '.') + } else { + base = d + } + } + return base + ('[]' * arrayDim) + } + + def accessToString = { int access -> + def parts = [] + if ((access & 0x0001) != 0) parts.add('public') + else if ((access & 0x0004) != 0) parts.add('protected') + else if ((access & 0x0002) != 0) parts.add('private') + if ((access & 0x0008) != 0) parts.add('static') + if ((access & 0x0010) != 0) parts.add('final') + if ((access & 0x0400) != 0) parts.add('abstract') + return parts.join(' ') + } + + def getClassKind = { node -> + int access = node.access + if ((access & 0x2000) != 0) return '@interface' // ACC_ANNOTATION + if ((access & 0x4000) != 0) return 'enum' // ACC_ENUM + if ((access & 0x0200) != 0) return 'interface' // ACC_INTERFACE + if ((access & 0x0400) != 0) return 'abstract class' + return 'class' + } + + def parseMethodParams = { String descriptor -> + def params = [] + def d = descriptor.substring(1) // skip '(' + while (!d.startsWith(')')) { + int arrayDim = 0 + while (d.startsWith('[')) { arrayDim++; d = d.substring(1) } + if (d.startsWith('L')) { + int end = d.indexOf(';') + params.add(d.substring(1, end).replace('/', '.') + ('[]' * arrayDim)) + d = d.substring(end + 1) + } else { + def base + switch (d.charAt(0)) { + case 'Z': base = 'boolean'; break + case 'B': base = 'byte'; break + case 'C': base = 'char'; break + case 'S': base = 'short'; break + case 'I': base = 'int'; break + case 'J': base = 'long'; break + case 'F': base = 'float'; break + case 'D': base = 'double'; break + default: base = String.valueOf(d.charAt(0)) + } + params.add(base + ('[]' * arrayDim)) + d = d.substring(1) + } + } + return params + } + + def getReturnType = { String descriptor -> + descriptorToType(descriptor.substring(descriptor.indexOf(')') + 1)) + } + + def getParamNames = { methodNode, int paramCount -> + def localVars = methodNode.localVariables + if (localVars) { + def isStatic = (methodNode.access & 0x0008) != 0 + def startIdx = isStatic ? 0 : 1 + def paramVars = localVars.findAll { it.index >= startIdx && it.index < startIdx + paramCount } + .sort { it.index } + if (paramVars.size() == paramCount) { + return paramVars.collect { it.name } + } + } + return (0.. + jar.entries().each { entry -> + if (entry.name.endsWith('.class') && !entry.name.startsWith('META-INF/')) { + try { + jar.getInputStream(entry).withStream { is -> + def reader = ClassReader.getConstructor(InputStream).newInstance(is) + def node = ClassNode.getDeclaredConstructor().newInstance() + reader.invokeMethod('accept', [node, 0] as Object[]) + if (node.name.startsWith('com/hypixel/hytale/')) { + classes.add(node) + } + } + } catch (Exception e) { + // Skip unreadable classes + } + } + } + } + println "Found ${classes.size()} classes in com.hypixel.hytale namespace" + + // ── Clean and prepare output directory ────────────────────────── + + if (outputDir.exists()) outputDir.deleteDir() + outputDir.mkdirs() + + // ── Group by top-level subpackage ─────────────────────────────── + + def grouped = classes.groupBy { node -> + def parts = node.name.split('/') + parts.length >= 4 ? parts[3] : '_root' + } + + // ── Write per-group files ─────────────────────────────────────── + + grouped.each { groupName, groupClasses -> + def outFile = new File(outputDir, "${groupName}.txt") + outFile.withWriter('UTF-8') { writer -> + writer.writeLine("# Hytale Server API: com.hypixel.hytale.${groupName}") + writer.writeLine("# Generated from HytaleServer.jar") + writer.writeLine("") + + groupClasses.sort { it.name }.each { node -> + def className = node.name.replace('/', '.') + def shortName = className.substring(className.lastIndexOf('.') + 1) + + // Skip anonymous inner classes + if (shortName.matches('.*\\$\\d+$')) return + + def packageName = className.substring(0, className.lastIndexOf('.')) + def kind = getClassKind(node) + // Strip 'abstract' from access — already captured in kind ("abstract class") + def accessStr = accessToString(node.access).replace('abstract ', '').trim() + + writer.writeLine('=' * 80) + writer.writeLine("package ${packageName}") + writer.writeLine("${accessStr} ${kind} ${shortName}") + + if (node.superName && node.superName != 'java/lang/Object') { + writer.writeLine(" extends ${node.superName.replace('/', '.')}") + } + if (node.interfaces && !node.interfaces.isEmpty()) { + def keyword = (node.access & 0x0200) != 0 ? 'extends' : 'implements' + writer.writeLine(" ${keyword} ${node.interfaces.collect { it.replace('/', '.') }.join(', ')}") + } + writer.writeLine("") + + // Enum constants + if ((node.access & 0x4000) != 0) { + def enumConsts = node.fields?.findAll { (it.access & 0x4000) != 0 } + if (enumConsts) { + writer.writeLine(" // Enum constants:") + enumConsts.each { f -> writer.writeLine(" ${f.name}") } + writer.writeLine("") + } + } + + // Fields (non-enum, non-synthetic) + def fields = node.fields?.findAll { f -> + (f.access & 0x4000) == 0 && (f.access & 0x1000) == 0 + } + if (fields) { + writer.writeLine(" // Fields:") + fields.each { f -> + writer.writeLine(" ${accessToString(f.access)} ${descriptorToType(f.desc)} ${f.name}") + } + writer.writeLine("") + } + + // Constructors + def ctors = node.methods?.findAll { it.name == '' && (it.access & 0x1000) == 0 } + if (ctors) { + writer.writeLine(" // Constructors:") + ctors.each { m -> + def params = parseMethodParams(m.desc) + def names = getParamNames(m, params.size()) + def paramStr = params.withIndex().collect { type, i -> "${type} ${names[i]}" }.join(', ') + writer.writeLine(" ${accessToString(m.access)} ${shortName}(${paramStr})") + } + writer.writeLine("") + } + + // Methods (non-init, non-synthetic, non-bridge) + def methods = node.methods?.findAll { m -> + m.name != '' && m.name != '' && + (m.access & 0x1000) == 0 && (m.access & 0x0040) == 0 + } + if (methods) { + writer.writeLine(" // Methods:") + methods.sort { it.name }.each { m -> + def ret = getReturnType(m.desc) + def params = parseMethodParams(m.desc) + def names = getParamNames(m, params.size()) + def paramStr = params.withIndex().collect { type, i -> "${type} ${names[i]}" }.join(', ') + writer.writeLine(" ${accessToString(m.access)} ${ret} ${m.name}(${paramStr})") + } + writer.writeLine("") + } + + writer.writeLine("") + } + } + println " ${outFile.name}: ${groupClasses.size()} classes" + } + + // ── Master index ──────────────────────────────────────────────── + + def indexFile = new File(outputDir, '_index.txt') + indexFile.withWriter('UTF-8') { writer -> + writer.writeLine("# Hytale Server API Index") + writer.writeLine("# Total classes: ${classes.size()}") + writer.writeLine("") + classes.sort { it.name }.each { node -> + writer.writeLine("${getClassKind(node)} ${node.name.replace('/', '.')}") + } + } + + println "API reference generated in ${outputDir}/ (${classes.size()} classes)" + } +} + +// Convenience alias +tasks.register('apiRef') { + description = 'Download server jar and generate API reference' + group = 'api-reference' + dependsOn 'dumpServerApi' } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 335beee..af5cc67 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,4 +1,4 @@ plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' } rootProject.name = 'HyghtmapMod' \ No newline at end of file diff --git a/src/main/java/net/wolvesfortress/heightmap/HyghtmapModPlugin.java b/src/main/java/net/wolvesfortress/heightmap/HyghtmapModPlugin.java index 4d5cf7d..5c6580c 100644 --- a/src/main/java/net/wolvesfortress/heightmap/HyghtmapModPlugin.java +++ b/src/main/java/net/wolvesfortress/heightmap/HyghtmapModPlugin.java @@ -1,10 +1,13 @@ package net.wolvesfortress.heightmap; +import com.hypixel.hytale.builtin.buildertools.tooloperations.ToolOperation; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; import com.hypixel.hytale.logger.HytaleLogger; +import net.wolvesfortress.heightmap.commands.FsuCommand; import net.wolvesfortress.heightmap.commands.HyghtmapModPluginCommand; +import com.hypixel.hytale.builtin.buildertools.tooloperations.LayerBrushOperation; import javax.annotation.Nonnull; import java.util.logging.Level; @@ -32,6 +35,9 @@ public static HyghtmapModPlugin getInstance() { @Override protected void setup() { + // Register custom tool operations + registerToolOperations(); + // Register commands registerCommands(); @@ -45,12 +51,48 @@ protected void setup() { private void registerCommands() { try { getCommandRegistry().registerCommand(new HyghtmapModPluginCommand()); - LOGGER.at(Level.INFO).log("[HyghtmapMod] Registered /heightmap command"); + getCommandRegistry().registerCommand(new FsuCommand()); + LOGGER.at(Level.INFO).log("[HyghtmapMod] Registered /heightmap and /fsu commands"); } catch (Exception e) { LOGGER.at(Level.WARNING).withCause(e).log("[HyghtmapMod] Failed to register commands"); } } + /** + * Register custom builder tool operations into the ToolOperation registry. + */ + @SuppressWarnings("unchecked") + private void registerToolOperations() { + try { + ToolOperation.OPERATIONS.put("LayerBrush", LayerBrushOperation::new); + LOGGER.at(Level.INFO).log("[HyghtmapMod] Registered LayerBrush tool operation"); + } catch (UnsupportedOperationException e) { + // Map might be unmodifiable — try reflection as fallback + LOGGER.at(Level.WARNING).log("[HyghtmapMod] OPERATIONS map is unmodifiable, attempting reflection fallback..."); + try { + java.lang.reflect.Field field = ToolOperation.class.getDeclaredField("OPERATIONS"); + field.setAccessible(true); + + // If wrapped in Collections.unmodifiableMap, the backing map is in a field called 'm' + Object mapObj = field.get(null); + if (mapObj instanceof java.util.Map) { + java.lang.reflect.Field mField = mapObj.getClass().getDeclaredField("m"); + mField.setAccessible(true); + @SuppressWarnings("unchecked") + java.util.Map backingMap = (java.util.Map) mField.get(mapObj); + backingMap.put("LayerBrush", (com.hypixel.hytale.builtin.buildertools.tooloperations.OperationFactory) LayerBrushOperation::new); + LOGGER.at(Level.INFO).log("[HyghtmapMod] Registered LayerBrush tool operation (via reflection)"); + } + } catch (Exception reflectionEx) { + LOGGER.at(Level.SEVERE).withCause(reflectionEx) + .log("[HyghtmapMod] Failed to register LayerBrush operation — tool will not work"); + } + } catch (Exception e) { + LOGGER.at(Level.SEVERE).withCause(e) + .log("[HyghtmapMod] Failed to register LayerBrush operation"); + } + } + /** * Register event listeners. */ diff --git a/src/main/resources/Common/UI/Custom/Pages/HeightmapImportPage.ui b/src/main/resources/Common/UI/Custom/Pages/HeightmapImportPage.ui index ba81298..e21b36f 100644 --- a/src/main/resources/Common/UI/Custom/Pages/HeightmapImportPage.ui +++ b/src/main/resources/Common/UI/Custom/Pages/HeightmapImportPage.ui @@ -16,7 +16,7 @@ Group #FormContainer { #Title { $C.@Title { - @Text = %server.customUI.heightmapImport.title; + @Text = hyghtmap.customUI.heightmapImport.title; } } @@ -27,7 +27,7 @@ Group #FormContainer { Label { Anchor: (Bottom: 12); Style: (...$C.@DefaultLabelStyle, Wrap: true, FontSize: 14, TextColor: #94969a); - Text: %server.customUI.heightmapImport.description; + Text: hyghtmap.customUI.heightmapImport.description; } Group { @@ -42,7 +42,7 @@ Group #FormContainer { Anchor: (Bottom: 10); Label { - Text: %server.customUI.heightmapImport.filePath; + Text: hyghtmap.customUI.heightmapImport.filePath; Anchor: (Bottom: 4); Style: $C.@DefaultLabelStyle; } @@ -60,7 +60,7 @@ Group #FormContainer { $C.@SecondaryTextButton #BrowseButton { @Anchor = (Width: 140); - Text: %server.customUI.browse; + Text: hyghtmap.customUI.browse; } } } @@ -71,7 +71,7 @@ Group #FormContainer { Anchor: (Bottom: 8, Height: 35); Label { - Text: %server.customUI.heightmapImport.heightScale; + Text: hyghtmap.customUI.heightmapImport.heightScale; Anchor: (Width: 140); Style: (...$C.@DefaultLabelStyle, VerticalAlignment: Center); } @@ -93,7 +93,7 @@ Group #FormContainer { Anchor: (Bottom: 8); Label { - Text: %server.customUI.heightmapImport.baseBlock; + Text: hyghtmap.customUI.heightmapImport.baseBlock; Anchor: (Bottom: 4); Style: $C.@DefaultLabelStyle; } @@ -111,7 +111,7 @@ Group #FormContainer { Anchor: (Bottom: 8, Height: 35); Label { - Text: %server.customUI.heightmapImport.importMode; + Text: hyghtmap.customUI.heightmapImport.importMode; Anchor: (Width: 140); Style: (...$C.@DefaultLabelStyle, VerticalAlignment: Center); } @@ -127,7 +127,7 @@ Group #FormContainer { Anchor: (Bottom: 8, Height: 35); Label { - Text: %server.customUI.heightmapImport.channel; + Text: hyghtmap.customUI.heightmapImport.channel; Anchor: (Width: 140); Style: (...$C.@DefaultLabelStyle, VerticalAlignment: Center); } @@ -144,7 +144,7 @@ Group #FormContainer { Visible: false; Label { - Text: %server.customUI.heightmapImport.colormapPath; + Text: hyghtmap.customUI.heightmapImport.colormapPath; Anchor: (Bottom: 4); Style: $C.@DefaultLabelStyle; } @@ -162,7 +162,7 @@ Group #FormContainer { $C.@SecondaryTextButton #BrowseButton { @Anchor = (Width: 140); - Text: %server.customUI.browse; + Text: hyghtmap.customUI.browse; } } } @@ -173,7 +173,7 @@ Group #FormContainer { Anchor: (Top: 4, Bottom: 8); $C.@CheckBoxWithLabel #InvertCheckbox { - @Text = %server.customUI.heightmapImport.invertHeight; + @Text = hyghtmap.customUI.heightmapImport.invertHeight; @Checked = false; } } @@ -184,7 +184,7 @@ Group #FormContainer { Anchor: (Top: 4, Bottom: 8); $C.@CheckBoxWithLabel #SmoothCheckbox { - @Text = %server.customUI.heightmapImport.smooth; + @Text = hyghtmap.customUI.heightmapImport.smooth; @Checked = false; } } @@ -195,7 +195,7 @@ Group #FormContainer { Anchor: (Bottom: 8, Height: 35); Label { - Text: %server.customUI.heightmapImport.origin; + Text: hyghtmap.customUI.heightmapImport.origin; Anchor: (Width: 140); Style: (...$C.@DefaultLabelStyle, VerticalAlignment: Center); } @@ -211,7 +211,7 @@ Group #FormContainer { Anchor: (Height: 35); Label { - Text: %server.customUI.heightmapImport.maxSize; + Text: hyghtmap.customUI.heightmapImport.maxSize; Anchor: (Width: 140); Style: (...$C.@DefaultLabelStyle, VerticalAlignment: Center); } @@ -245,7 +245,7 @@ Group #FormContainer { Anchor: (Top: 6); $C.@TextButton #ImportButton { - Text: %server.customUI.heightmapImport.importButton; + Text: hyghtmap.customUI.heightmapImport.importButton; } } } @@ -262,7 +262,7 @@ $C.@PageOverlay #BrowserPage { #Title { $C.@Title { - @Text = %server.customUI.heightmapImport.browserTitle; + @Text = hyghtmap.customUI.heightmapImport.browserTitle; } } @@ -289,7 +289,7 @@ $C.@PageOverlay #BrowserPage { $C.@TextField #SearchInput { @Anchor = (Left: 0); FlexWeight: 1; - PlaceholderText: %server.customUI.heightmapImport.searchPlaceholder; + PlaceholderText: hyghtmap.customUI.heightmapImport.searchPlaceholder; } } @@ -308,14 +308,14 @@ $C.@PageOverlay #BrowserPage { $C.@TextButton #SelectButton { @Sounds = $Sounds.@SaveSettings; @Anchor = (Right: 4); - Text: %server.customUI.select; + Text: hyghtmap.customUI.select; FlexWeight: 1; } $C.@SecondaryTextButton #CancelButton { @Sounds = $Sounds.@ButtonsCancel; @Anchor = (Left: 4); - Text: %server.customUI.cancel; + Text: hyghtmap.customUI.cancel; FlexWeight: 1; } } @@ -333,7 +333,7 @@ $C.@PageOverlay #ColormapBrowserPage { #Title { $C.@Title { - @Text = %server.customUI.heightmapImport.colormapBrowserTitle; + @Text = hyghtmap.customUI.heightmapImport.colormapBrowserTitle; } } @@ -360,7 +360,7 @@ $C.@PageOverlay #ColormapBrowserPage { $C.@TextField #SearchInput { @Anchor = (Left: 0); FlexWeight: 1; - PlaceholderText: %server.customUI.heightmapImport.searchPlaceholder; + PlaceholderText: hyghtmap.customUI.heightmapImport.searchPlaceholder; } } @@ -379,14 +379,14 @@ $C.@PageOverlay #ColormapBrowserPage { $C.@TextButton #SelectButton { @Sounds = $Sounds.@SaveSettings; @Anchor = (Right: 4); - Text: %server.customUI.select; + Text: hyghtmap.customUI.select; FlexWeight: 1; } $C.@SecondaryTextButton #CancelButton { @Sounds = $Sounds.@ButtonsCancel; @Anchor = (Left: 4); - Text: %server.customUI.cancel; + Text: hyghtmap.customUI.cancel; FlexWeight: 1; } } diff --git a/src/main/resources/Server/Languages/en-US/hyghtmap.lang b/src/main/resources/Server/Languages/en-US/hyghtmap.lang new file mode 100644 index 0000000..8d5c550 --- /dev/null +++ b/src/main/resources/Server/Languages/en-US/hyghtmap.lang @@ -0,0 +1,40 @@ +# Heightmap Import UI +customUI.heightmapImport.title = Heightmap Import +customUI.heightmapImport.description = Import a heightmap image as blocks. Supports PNG, BMP, JPEG, and raw F16/F32 formats. +customUI.heightmapImport.filePath = Heightmap File +customUI.heightmapImport.colormapPath = Colormap File +customUI.heightmapImport.heightScale = Height Scale +customUI.heightmapImport.maxSize = Max Size +customUI.heightmapImport.baseBlock = Block Pattern +customUI.heightmapImport.importMode = Import Mode +customUI.heightmapImport.channel = Height Channel +customUI.heightmapImport.origin = Origin +customUI.heightmapImport.invertHeight = Invert Height +customUI.heightmapImport.smooth = Smooth Pass +customUI.heightmapImport.importButton = Import +customUI.heightmapImport.browserTitle = Select Heightmap +customUI.heightmapImport.colormapBrowserTitle = Select Colormap +customUI.heightmapImport.searchPlaceholder = Search files... + +# Import modes +customUI.heightmapImport.mode.heightmap = Heightmap (Solid) +customUI.heightmapImport.mode.surface = Surface Only +customUI.heightmapImport.mode.colormap = Colormap (Flat) +customUI.heightmapImport.mode.normalmap = Normal Map + +# Channels +customUI.heightmapImport.channel.luminance = Luminance +customUI.heightmapImport.channel.red = Red +customUI.heightmapImport.channel.green = Green +customUI.heightmapImport.channel.blue = Blue +customUI.heightmapImport.channel.alpha = Alpha + +# Origins +customUI.origin.bottom_front_left = Bottom Front Left +customUI.origin.bottom_center = Bottom Center +customUI.origin.center = Center +customUI.origin.top_center = Top Center + +# Success message +heightmapMod.heightmapImport.success = Heightmap imported: {count} blocks ({width}x{height}x{depth}) copied to clipboard. + diff --git a/src/main/resources/Server/Languages/en-US/net.wolvesfortress.heightmap.lang b/src/main/resources/Server/Languages/en-US/net.wolvesfortress.heightmap.lang deleted file mode 100644 index 11c94fe..0000000 --- a/src/main/resources/Server/Languages/en-US/net.wolvesfortress.heightmap.lang +++ /dev/null @@ -1,33 +0,0 @@ -# Heightmap Import UI -server.customUI.heightmapImport.title = Heightmap Import -server.customUI.heightmapImport.description = Import a heightmap image as blocks. Supports PNG, BMP, JPEG, and raw F16/F32 formats. -server.customUI.heightmapImport.filePath = Heightmap File -server.customUI.heightmapImport.colormapPath = Colormap File -server.customUI.heightmapImport.heightScale = Height Scale -server.customUI.heightmapImport.maxSize = Max Size -server.customUI.heightmapImport.baseBlock = Block Pattern -server.customUI.heightmapImport.importMode = Import Mode -server.customUI.heightmapImport.channel = Height Channel -server.customUI.heightmapImport.origin = Origin -server.customUI.heightmapImport.invertHeight = Invert Height -server.customUI.heightmapImport.smooth = Smooth Pass -server.customUI.heightmapImport.importButton = Import -server.customUI.heightmapImport.browserTitle = Select Heightmap -server.customUI.heightmapImport.colormapBrowserTitle = Select Colormap -server.customUI.heightmapImport.searchPlaceholder = Search files... - -# Import modes -server.customUI.heightmapImport.mode.heightmap = Heightmap (Solid) -server.customUI.heightmapImport.mode.surface = Surface Only -server.customUI.heightmapImport.mode.colormap = Colormap (Flat) -server.customUI.heightmapImport.mode.normalmap = Normal Map - -# Channels -server.customUI.heightmapImport.channel.luminance = Luminance -server.customUI.heightmapImport.channel.red = Red -server.customUI.heightmapImport.channel.green = Green -server.customUI.heightmapImport.channel.blue = Blue -server.customUI.heightmapImport.channel.alpha = Alpha - -# Success message -server.heightmapMod.heightmapImport.success = Heightmap imported: {count} blocks ({width}x{height}x{depth}) copied to clipboard. \ No newline at end of file diff --git a/src/main/resources/manifest.json b/src/main/resources/manifest.json index 420c76a..d07ab29 100644 --- a/src/main/resources/manifest.json +++ b/src/main/resources/manifest.json @@ -6,5 +6,6 @@ "Authors": [{"Name": "inxomnyaa"}], "Main": "net.wolvesfortress.heightmap.HyghtmapModPlugin", "LoadOrder": "POSTWORLD", - "IncludesAssetPack": true + "IncludesAssetPack": false, + "ServerVersion": "2026.02.19-1a311a592" } \ No newline at end of file From 6974a8432bd20f331b74e76c7c3aab02c9610c86 Mon Sep 17 00:00:00 2001 From: Capsup Date: Wed, 4 Mar 2026 21:54:51 +0100 Subject: [PATCH 2/4] fix: remove imports --- build.gradle | 4 +- .../heightmap/HyghtmapModPlugin.java | 39 +------------------ 2 files changed, 3 insertions(+), 40 deletions(-) diff --git a/build.gradle b/build.gradle index 2cde67c..958ed3e 100644 --- a/build.gradle +++ b/build.gradle @@ -70,7 +70,7 @@ tasks.register('watch') { // ── API Reference Generation ──────────────────────────────────────────────── // Run: ./gradlew apiRef -// Downloads HytaleServer.jar and generates plain-text API reference files +// Uses HytaleServer.jar to generate plain-text API reference files // in .hytale-api/ for quick lookup during development. configurations { @@ -340,7 +340,7 @@ tasks.register('dumpServerApi') { // Convenience alias tasks.register('apiRef') { - description = 'Download server jar and generate API reference' + description = 'Use server jar and generate API reference' group = 'api-reference' dependsOn 'dumpServerApi' } \ No newline at end of file diff --git a/src/main/java/net/wolvesfortress/heightmap/HyghtmapModPlugin.java b/src/main/java/net/wolvesfortress/heightmap/HyghtmapModPlugin.java index 5c6580c..9bcf17e 100644 --- a/src/main/java/net/wolvesfortress/heightmap/HyghtmapModPlugin.java +++ b/src/main/java/net/wolvesfortress/heightmap/HyghtmapModPlugin.java @@ -1,11 +1,9 @@ package net.wolvesfortress.heightmap; -import com.hypixel.hytale.builtin.buildertools.tooloperations.ToolOperation; import com.hypixel.hytale.server.core.plugin.JavaPlugin; import com.hypixel.hytale.server.core.plugin.JavaPluginInit; import com.hypixel.hytale.logger.HytaleLogger; -import net.wolvesfortress.heightmap.commands.FsuCommand; import net.wolvesfortress.heightmap.commands.HyghtmapModPluginCommand; import com.hypixel.hytale.builtin.buildertools.tooloperations.LayerBrushOperation; @@ -52,47 +50,12 @@ private void registerCommands() { try { getCommandRegistry().registerCommand(new HyghtmapModPluginCommand()); getCommandRegistry().registerCommand(new FsuCommand()); - LOGGER.at(Level.INFO).log("[HyghtmapMod] Registered /heightmap and /fsu commands"); + LOGGER.at(Level.INFO).log("[HyghtmapMod] Registered /heightmap"); } catch (Exception e) { LOGGER.at(Level.WARNING).withCause(e).log("[HyghtmapMod] Failed to register commands"); } } - /** - * Register custom builder tool operations into the ToolOperation registry. - */ - @SuppressWarnings("unchecked") - private void registerToolOperations() { - try { - ToolOperation.OPERATIONS.put("LayerBrush", LayerBrushOperation::new); - LOGGER.at(Level.INFO).log("[HyghtmapMod] Registered LayerBrush tool operation"); - } catch (UnsupportedOperationException e) { - // Map might be unmodifiable — try reflection as fallback - LOGGER.at(Level.WARNING).log("[HyghtmapMod] OPERATIONS map is unmodifiable, attempting reflection fallback..."); - try { - java.lang.reflect.Field field = ToolOperation.class.getDeclaredField("OPERATIONS"); - field.setAccessible(true); - - // If wrapped in Collections.unmodifiableMap, the backing map is in a field called 'm' - Object mapObj = field.get(null); - if (mapObj instanceof java.util.Map) { - java.lang.reflect.Field mField = mapObj.getClass().getDeclaredField("m"); - mField.setAccessible(true); - @SuppressWarnings("unchecked") - java.util.Map backingMap = (java.util.Map) mField.get(mapObj); - backingMap.put("LayerBrush", (com.hypixel.hytale.builtin.buildertools.tooloperations.OperationFactory) LayerBrushOperation::new); - LOGGER.at(Level.INFO).log("[HyghtmapMod] Registered LayerBrush tool operation (via reflection)"); - } - } catch (Exception reflectionEx) { - LOGGER.at(Level.SEVERE).withCause(reflectionEx) - .log("[HyghtmapMod] Failed to register LayerBrush operation — tool will not work"); - } - } catch (Exception e) { - LOGGER.at(Level.SEVERE).withCause(e) - .log("[HyghtmapMod] Failed to register LayerBrush operation"); - } - } - /** * Register event listeners. */ From 1c87726756cb725389503e3de7a3315075cdc3c8 Mon Sep 17 00:00:00 2001 From: Capsup Date: Wed, 4 Mar 2026 21:55:56 +0100 Subject: [PATCH 3/4] fix: remove missing commands --- .../java/net/wolvesfortress/heightmap/HyghtmapModPlugin.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/net/wolvesfortress/heightmap/HyghtmapModPlugin.java b/src/main/java/net/wolvesfortress/heightmap/HyghtmapModPlugin.java index 9bcf17e..132e66a 100644 --- a/src/main/java/net/wolvesfortress/heightmap/HyghtmapModPlugin.java +++ b/src/main/java/net/wolvesfortress/heightmap/HyghtmapModPlugin.java @@ -5,7 +5,6 @@ import com.hypixel.hytale.logger.HytaleLogger; import net.wolvesfortress.heightmap.commands.HyghtmapModPluginCommand; -import com.hypixel.hytale.builtin.buildertools.tooloperations.LayerBrushOperation; import javax.annotation.Nonnull; import java.util.logging.Level; @@ -33,9 +32,6 @@ public static HyghtmapModPlugin getInstance() { @Override protected void setup() { - // Register custom tool operations - registerToolOperations(); - // Register commands registerCommands(); @@ -49,7 +45,6 @@ protected void setup() { private void registerCommands() { try { getCommandRegistry().registerCommand(new HyghtmapModPluginCommand()); - getCommandRegistry().registerCommand(new FsuCommand()); LOGGER.at(Level.INFO).log("[HyghtmapMod] Registered /heightmap"); } catch (Exception e) { LOGGER.at(Level.WARNING).withCause(e).log("[HyghtmapMod] Failed to register commands"); From 447157c6c3749b5c81f4c54b5b1a07c42ac55e38 Mon Sep 17 00:00:00 2001 From: Capsup Date: Wed, 4 Mar 2026 22:24:01 +0100 Subject: [PATCH 4/4] fix: labels in-game --- .../UI/Custom/Pages/HeightmapImportPage.ui | 46 +++++++++---------- .../Server/Languages/en-US/hyghtmap.lang | 5 ++ src/main/resources/manifest.json | 2 +- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/main/resources/Common/UI/Custom/Pages/HeightmapImportPage.ui b/src/main/resources/Common/UI/Custom/Pages/HeightmapImportPage.ui index e21b36f..09310ad 100644 --- a/src/main/resources/Common/UI/Custom/Pages/HeightmapImportPage.ui +++ b/src/main/resources/Common/UI/Custom/Pages/HeightmapImportPage.ui @@ -16,7 +16,7 @@ Group #FormContainer { #Title { $C.@Title { - @Text = hyghtmap.customUI.heightmapImport.title; + @Text = %hyghtmap.customUI.heightmapImport.title; } } @@ -27,7 +27,7 @@ Group #FormContainer { Label { Anchor: (Bottom: 12); Style: (...$C.@DefaultLabelStyle, Wrap: true, FontSize: 14, TextColor: #94969a); - Text: hyghtmap.customUI.heightmapImport.description; + Text: %hyghtmap.customUI.heightmapImport.description; } Group { @@ -42,7 +42,7 @@ Group #FormContainer { Anchor: (Bottom: 10); Label { - Text: hyghtmap.customUI.heightmapImport.filePath; + Text: %hyghtmap.customUI.heightmapImport.filePath; Anchor: (Bottom: 4); Style: $C.@DefaultLabelStyle; } @@ -60,7 +60,7 @@ Group #FormContainer { $C.@SecondaryTextButton #BrowseButton { @Anchor = (Width: 140); - Text: hyghtmap.customUI.browse; + Text: %hyghtmap.customUI.browse; } } } @@ -71,7 +71,7 @@ Group #FormContainer { Anchor: (Bottom: 8, Height: 35); Label { - Text: hyghtmap.customUI.heightmapImport.heightScale; + Text: %hyghtmap.customUI.heightmapImport.heightScale; Anchor: (Width: 140); Style: (...$C.@DefaultLabelStyle, VerticalAlignment: Center); } @@ -93,7 +93,7 @@ Group #FormContainer { Anchor: (Bottom: 8); Label { - Text: hyghtmap.customUI.heightmapImport.baseBlock; + Text: %hyghtmap.customUI.heightmapImport.baseBlock; Anchor: (Bottom: 4); Style: $C.@DefaultLabelStyle; } @@ -111,7 +111,7 @@ Group #FormContainer { Anchor: (Bottom: 8, Height: 35); Label { - Text: hyghtmap.customUI.heightmapImport.importMode; + Text: %hyghtmap.customUI.heightmapImport.importMode; Anchor: (Width: 140); Style: (...$C.@DefaultLabelStyle, VerticalAlignment: Center); } @@ -127,7 +127,7 @@ Group #FormContainer { Anchor: (Bottom: 8, Height: 35); Label { - Text: hyghtmap.customUI.heightmapImport.channel; + Text: %hyghtmap.customUI.heightmapImport.channel; Anchor: (Width: 140); Style: (...$C.@DefaultLabelStyle, VerticalAlignment: Center); } @@ -144,7 +144,7 @@ Group #FormContainer { Visible: false; Label { - Text: hyghtmap.customUI.heightmapImport.colormapPath; + Text: %hyghtmap.customUI.heightmapImport.colormapPath; Anchor: (Bottom: 4); Style: $C.@DefaultLabelStyle; } @@ -162,7 +162,7 @@ Group #FormContainer { $C.@SecondaryTextButton #BrowseButton { @Anchor = (Width: 140); - Text: hyghtmap.customUI.browse; + Text: %hyghtmap.customUI.browse; } } } @@ -173,7 +173,7 @@ Group #FormContainer { Anchor: (Top: 4, Bottom: 8); $C.@CheckBoxWithLabel #InvertCheckbox { - @Text = hyghtmap.customUI.heightmapImport.invertHeight; + @Text = %hyghtmap.customUI.heightmapImport.invertHeight; @Checked = false; } } @@ -184,7 +184,7 @@ Group #FormContainer { Anchor: (Top: 4, Bottom: 8); $C.@CheckBoxWithLabel #SmoothCheckbox { - @Text = hyghtmap.customUI.heightmapImport.smooth; + @Text = %hyghtmap.customUI.heightmapImport.smooth; @Checked = false; } } @@ -195,7 +195,7 @@ Group #FormContainer { Anchor: (Bottom: 8, Height: 35); Label { - Text: hyghtmap.customUI.heightmapImport.origin; + Text: %hyghtmap.customUI.heightmapImport.origin; Anchor: (Width: 140); Style: (...$C.@DefaultLabelStyle, VerticalAlignment: Center); } @@ -211,7 +211,7 @@ Group #FormContainer { Anchor: (Height: 35); Label { - Text: hyghtmap.customUI.heightmapImport.maxSize; + Text: %hyghtmap.customUI.heightmapImport.maxSize; Anchor: (Width: 140); Style: (...$C.@DefaultLabelStyle, VerticalAlignment: Center); } @@ -245,7 +245,7 @@ Group #FormContainer { Anchor: (Top: 6); $C.@TextButton #ImportButton { - Text: hyghtmap.customUI.heightmapImport.importButton; + Text: %hyghtmap.customUI.heightmapImport.importButton; } } } @@ -262,7 +262,7 @@ $C.@PageOverlay #BrowserPage { #Title { $C.@Title { - @Text = hyghtmap.customUI.heightmapImport.browserTitle; + @Text = %hyghtmap.customUI.heightmapImport.browserTitle; } } @@ -289,7 +289,7 @@ $C.@PageOverlay #BrowserPage { $C.@TextField #SearchInput { @Anchor = (Left: 0); FlexWeight: 1; - PlaceholderText: hyghtmap.customUI.heightmapImport.searchPlaceholder; + PlaceholderText: %hyghtmap.customUI.heightmapImport.searchPlaceholder; } } @@ -308,14 +308,14 @@ $C.@PageOverlay #BrowserPage { $C.@TextButton #SelectButton { @Sounds = $Sounds.@SaveSettings; @Anchor = (Right: 4); - Text: hyghtmap.customUI.select; + Text: %hyghtmap.customUI.select; FlexWeight: 1; } $C.@SecondaryTextButton #CancelButton { @Sounds = $Sounds.@ButtonsCancel; @Anchor = (Left: 4); - Text: hyghtmap.customUI.cancel; + Text: %hyghtmap.customUI.cancel; FlexWeight: 1; } } @@ -333,7 +333,7 @@ $C.@PageOverlay #ColormapBrowserPage { #Title { $C.@Title { - @Text = hyghtmap.customUI.heightmapImport.colormapBrowserTitle; + @Text = %hyghtmap.customUI.heightmapImport.colormapBrowserTitle; } } @@ -360,7 +360,7 @@ $C.@PageOverlay #ColormapBrowserPage { $C.@TextField #SearchInput { @Anchor = (Left: 0); FlexWeight: 1; - PlaceholderText: hyghtmap.customUI.heightmapImport.searchPlaceholder; + PlaceholderText: %hyghtmap.customUI.heightmapImport.searchPlaceholder; } } @@ -379,14 +379,14 @@ $C.@PageOverlay #ColormapBrowserPage { $C.@TextButton #SelectButton { @Sounds = $Sounds.@SaveSettings; @Anchor = (Right: 4); - Text: hyghtmap.customUI.select; + Text: %hyghtmap.customUI.select; FlexWeight: 1; } $C.@SecondaryTextButton #CancelButton { @Sounds = $Sounds.@ButtonsCancel; @Anchor = (Left: 4); - Text: hyghtmap.customUI.cancel; + Text: %hyghtmap.customUI.cancel; FlexWeight: 1; } } diff --git a/src/main/resources/Server/Languages/en-US/hyghtmap.lang b/src/main/resources/Server/Languages/en-US/hyghtmap.lang index 8d5c550..28ac1ee 100644 --- a/src/main/resources/Server/Languages/en-US/hyghtmap.lang +++ b/src/main/resources/Server/Languages/en-US/hyghtmap.lang @@ -16,6 +16,11 @@ customUI.heightmapImport.browserTitle = Select Heightmap customUI.heightmapImport.colormapBrowserTitle = Select Colormap customUI.heightmapImport.searchPlaceholder = Search files... +# Common UI actions +customUI.browse = Browse +customUI.select = Select +customUI.cancel = Cancel + # Import modes customUI.heightmapImport.mode.heightmap = Heightmap (Solid) customUI.heightmapImport.mode.surface = Surface Only diff --git a/src/main/resources/manifest.json b/src/main/resources/manifest.json index d07ab29..6810e3c 100644 --- a/src/main/resources/manifest.json +++ b/src/main/resources/manifest.json @@ -6,6 +6,6 @@ "Authors": [{"Name": "inxomnyaa"}], "Main": "net.wolvesfortress.heightmap.HyghtmapModPlugin", "LoadOrder": "POSTWORLD", - "IncludesAssetPack": false, + "IncludesAssetPack": true, "ServerVersion": "2026.02.19-1a311a592" } \ No newline at end of file