diff --git a/de.peeeq.wurstscript/build.gradle b/de.peeeq.wurstscript/build.gradle index 5dd13708e..ff3d4a93d 100644 --- a/de.peeeq.wurstscript/build.gradle +++ b/de.peeeq.wurstscript/build.gradle @@ -156,7 +156,7 @@ dependencies { implementation 'com.github.albfernandez:juniversalchardet:2.4.0' implementation 'org.xerial:sqlite-jdbc:3.46.1.3' implementation 'com.github.inwc3:JMPQ3:v2.0.1' - implementation 'com.github.inwc3:wc3libs:9a11e1f089' + implementation 'com.github.inwc3:wc3libs:0542c41400' implementation 'com.github.wurstscript:wurst-project-config:348fcd4ef5' implementation 'org.slf4j:slf4j-api:2.0.17' implementation 'ch.qos.logback:logback-classic:1.5.20' diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/ProjectConfigBuilder.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/ProjectConfigBuilder.java index c3ecc68cb..be35bbb17 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/ProjectConfigBuilder.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/ProjectConfigBuilder.java @@ -8,6 +8,7 @@ import de.peeeq.wurstio.mpq.MpqEditor; import de.peeeq.wurstio.mpq.MpqEditorFactory; import de.peeeq.wurstio.utils.W3InstallationData; +import de.peeeq.wurstscript.CompileTimeInfo; import de.peeeq.wurstscript.RunArgs; import de.peeeq.wurstscript.WLogger; import net.moonlightflower.wc3libs.bin.app.MapFlag; @@ -115,7 +116,7 @@ public static MapRequest.CompilationResult apply(WurstProjectConfigData projectC result.w3i = new File(buildDir, "war3map.w3i"); applyW3IVersion(WurstBuildConfig.fromProject(projectConfig, null), w3I, runArgs.isLua()); - w3I.write(result.w3i); + w3I.write(result.w3i, W3I.EncodingFormat.AS_DEFINED); // Apply map header (this is cheap, so we always do it) applyMapHeader(projectConfig, targetMap, w3I.getPlayers().size(), w3I.getMapName(), w3I.getFlags().toInt()); @@ -201,6 +202,9 @@ private static String calculateProjectConfigHash(WurstProjectConfigData projectC WurstBuildConfig buildConfig = buildConfigFromBuildDir(buildDir); sb.append("scriptMode:").append(buildConfig.scriptMode()).append("\n"); sb.append("wc3Patch:").append(buildConfig.wc3PatchName()).append("\n"); + // Reapply metadata after compiler upgrades; W3I serialization rules can change + // without any project configuration changing. + sb.append("compilerVersion:").append(CompileTimeInfo.version).append("\n"); return ImportFile.calculateHash(sb.toString().getBytes(StandardCharsets.UTF_8)); } catch (Exception e) { @@ -241,43 +245,21 @@ private static GameVersion effectiveConfigInjectionVersion(File buildDir, W3Inst .orElseGet(buildConfig::fallbackGameVersion); } - static void applyW3IVersion(WurstBuildConfig buildConfig, W3I w3I, boolean lua) { + public static void applyW3IVersion(WurstBuildConfig buildConfig, W3I w3I, boolean lua) { if (lua) { WLogger.info("Applying lua w3i config"); w3I.setScriptLang(W3I.ScriptLang.LUA); } - // Keep the version from the source map unless the project pins a target patch. - // In that case, emit the newest W3I format supported by that patch. - buildConfig.configuredGameVersion().ifPresent(version -> { - int maxVersion = maxW3IVersionFor(version); - w3I.setFileVersion(lua - ? Math.max(maxVersion, W3I.EncodingFormat.W3I_0x1F.getVersion()) - : maxVersion); - }); - - // Lua map metadata needs the script-language field, which is absent in older formats. + // wc3Patch selects the target game version; it does not select the W3I encoding. + // Keep the source encoding so binary fields read from the map remain available. + // When build config adds fields that require a newer W3I format, promote the format + // at the point where those fields are applied. if (lua && w3I.getFileVersion() < W3I.EncodingFormat.W3I_0x1F.getVersion()) { w3I.setFileVersion(W3I.EncodingFormat.W3I_0x1F.getVersion()); } } - private static int maxW3IVersionFor(GameVersion version) { - if (version.compareTo(new GameVersion("1.31")) < 0) { - return W3I.EncodingFormat.W3I_0x19.getVersion(); - } - if (version.compareTo(new GameVersion("1.32")) < 0) { - return W3I.EncodingFormat.W3I_0x1C.getVersion(); - } - if (version.compareTo(new GameVersion("2.0")) < 0) { - return W3I.EncodingFormat.W3I_0x1F.getVersion(); - } - if (version.compareTo(new GameVersion("3.0")) < 0) { - return W3I.EncodingFormat.W3I_0x21.getVersion(); - } - return W3I.EncodingFormat.W3I_0x27.getVersion(); - } - private static WurstBuildConfig buildConfigFromBuildDir(File buildDir) { java.nio.file.Path projectRoot = buildDir.toPath().getParent(); if (projectRoot == null) { diff --git a/de.peeeq.wurstscript/src/test/java/de/peeeq/wurstio/languageserver/ProjectConfigBuilderTests.java b/de.peeeq.wurstscript/src/test/java/de/peeeq/wurstio/languageserver/ProjectConfigBuilderTests.java index 3e0ca374f..47e44ca4d 100644 --- a/de.peeeq.wurstscript/src/test/java/de/peeeq/wurstio/languageserver/ProjectConfigBuilderTests.java +++ b/de.peeeq.wurstscript/src/test/java/de/peeeq/wurstio/languageserver/ProjectConfigBuilderTests.java @@ -11,11 +11,11 @@ public class ProjectConfigBuilderTests { @Test - public void pinnedPatchSelectsNewestSupportedW3iFormat() throws Exception { - assertW3IVersion("1.30", W3I.EncodingFormat.W3I_0x19.getVersion()); - assertW3IVersion("1.31", W3I.EncodingFormat.W3I_0x1C.getVersion()); - assertW3IVersion("1.32", W3I.EncodingFormat.W3I_0x1F.getVersion()); - assertW3IVersion("2.0", W3I.EncodingFormat.W3I_0x21.getVersion()); + public void pinnedPatchDoesNotChangeSourceW3iFormat() throws Exception { + assertW3IVersion("1.30", W3I.EncodingFormat.W3I_0x27.getVersion()); + assertW3IVersion("1.31", W3I.EncodingFormat.W3I_0x27.getVersion()); + assertW3IVersion("1.32", W3I.EncodingFormat.W3I_0x27.getVersion()); + assertW3IVersion("2.0", W3I.EncodingFormat.W3I_0x27.getVersion()); assertW3IVersion("3.0", W3I.EncodingFormat.W3I_0x27.getVersion()); } diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/MapRequestPatchTargetTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/MapRequestPatchTargetTests.java index 02804490d..52a965d79 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/MapRequestPatchTargetTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/MapRequestPatchTargetTests.java @@ -1,12 +1,14 @@ package tests.wurstscript.tests; import de.peeeq.wurstio.languageserver.ModelManager; +import de.peeeq.wurstio.languageserver.ProjectConfigBuilder; import de.peeeq.wurstio.languageserver.WFile; import de.peeeq.wurstio.languageserver.WurstBuildConfig; import de.peeeq.wurstio.languageserver.requests.MapRequest; import de.peeeq.wurstio.languageserver.requests.RunMap; import de.peeeq.wurstscript.RunArgs; import de.peeeq.wurstio.utils.W3InstallationData; +import net.moonlightflower.wc3libs.bin.app.W3I; import net.moonlightflower.wc3libs.port.GameVersion; import org.testng.annotations.Test; @@ -41,6 +43,28 @@ public void pinnedBuildUsesWurstBuildPatchWithoutWarcraftExeDiscovery() throws E assertFalse(request.gameExe().isPresent(), "Pinned build requests should not need a Warcraft executable"); } + @Test + public void pinnedV3BuildKeepsFormat33SourceForJassAndLua() throws Exception { + Path project = projectWithPatch("v3.0"); + WurstBuildConfig config = WurstBuildConfig.fromWorkspaceRoot(WFile.create(project.toFile())); + int format33 = W3I.EncodingFormat.W3I_0x21.getVersion(); + + for (boolean lua : List.of(false, true)) { + W3I source = new W3I(); + source.setFileVersion(format33); + Path sourceMap = Files.createTempDirectory("w3i-v3-source"); + source.write(sourceMap.resolve(W3I.GAME_PATH.getName()).toFile()); + + W3I built = new W3I(Files.readAllBytes(sourceMap.resolve(W3I.GAME_PATH.getName()))); + ProjectConfigBuilder.applyW3IVersion(config, built, lua); + Path output = Files.createTempFile("w3i-v3-built", ".w3i"); + built.write(output.toFile()); + + assertEquals(new W3I(Files.readAllBytes(output)).getFileVersion(), format33, + "v3.0 must preserve a format-33 source W3I (lua=" + lua + ")"); + } + } + @Test public void pinnedExplicitGameExeUsesPatchVersionWithoutParsingExecutable() throws Exception { Path project = projectWithPatch("TFT-v1.31.1.12173");