Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion de.peeeq.wurstscript/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;

Expand Down Expand Up @@ -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");
Expand Down
Loading