From 18a9e5beecaecc3d6364dd563f13b8b7b7e42590 Mon Sep 17 00:00:00 2001 From: Frotty Date: Wed, 27 May 2026 14:29:05 +0200 Subject: [PATCH 1/3] better patch level support --- .../languageserver/ModelManagerImpl.java | 51 +++-- .../languageserver/ProjectConfigBuilder.java | 19 +- .../languageserver/WurstBuildConfig.java | 206 ++++++++++++++++++ .../wurstio/languageserver/WurstCommands.java | 7 +- .../languageserver/requests/MapRequest.java | 3 +- .../languageserver/requests/RunMap.java | 32 +-- .../wurstio/utils/W3InstallationData.java | 8 +- .../tests/WurstBuildConfigTests.java | 54 +++++ 8 files changed, 341 insertions(+), 39 deletions(-) create mode 100644 de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/WurstBuildConfig.java create mode 100644 de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstBuildConfigTests.java diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/ModelManagerImpl.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/ModelManagerImpl.java index adf855562..73b5de9be 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/ModelManagerImpl.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/ModelManagerImpl.java @@ -443,23 +443,26 @@ private Set providedPackages(CompilationUnit c) { } private CompilationUnit compileFromJar(WurstGui gui, String filename) throws IOException { - InputStream source = this.getClass().getResourceAsStream("/" + filename); - File sourceFile; - if (source == null) { - WLogger.severe("could not find " + filename + " in jar"); - System.err.println("could not find " + filename + " in jar"); - sourceFile = new File("./resources/" + filename); + File sourceFile = findProjectCoreJassFile(filename).orElse(null); + if (sourceFile != null) { + sourceFile = copyCoreJassToBuildRoot(sourceFile, filename); } else { - try { - File buildDir = getBuildDir(); - //noinspection ResultOfMethodCallIgnored - buildDir.mkdirs(); - sourceFile = new File(buildDir, filename); - if (!sourceFile.exists()) { + InputStream source = this.getClass().getResourceAsStream("/" + filename); + if (source == null) { + WLogger.severe("could not find " + filename + " in jar"); + System.err.println("could not find " + filename + " in jar"); + sourceFile = new File("./resources/" + filename); + } else { + try { + File buildDir = getBuildDir(); + //noinspection ResultOfMethodCallIgnored + buildDir.mkdirs(); + sourceFile = new File(buildDir, filename); java.nio.file.Files.copy(source, sourceFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + WLogger.info("Loaded bundled fallback " + filename); + } finally { + source.close(); } - } finally { - source.close(); } } @@ -472,6 +475,26 @@ private CompilationUnit compileFromJar(WurstGui gui, String filename) throws IOE } } + private Optional findProjectCoreJassFile(String filename) { + File projectCopy = new File(getBuildDir(), filename); + if (projectCopy.exists()) { + return Optional.of(projectCopy); + } + return Optional.empty(); + } + + private File copyCoreJassToBuildRoot(File sourceFile, String filename) throws IOException { + File buildDir = getBuildDir(); + //noinspection ResultOfMethodCallIgnored + buildDir.mkdirs(); + File buildCopy = new File(buildDir, filename); + if (!sourceFile.getCanonicalFile().equals(buildCopy.getCanonicalFile())) { + java.nio.file.Files.copy(sourceFile.toPath(), buildCopy.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + WLogger.info("Loaded project " + filename + " from " + sourceFile.getAbsolutePath()); + return buildCopy; + } + private File getBuildDir() { return new File(projectPath, "_build"); } 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 a59ac87a6..ac8088e24 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 @@ -52,7 +52,7 @@ public static MapRequest.CompilationResult apply(WurstProjectConfigData projectC result.script = mapScript; // Calculate hash of the project config for caching - String configHash = calculateProjectConfigHash(projectConfig); + String configHash = calculateProjectConfigHash(projectConfig, buildDir); W3I w3I; boolean configNeedsApplying = false; @@ -124,7 +124,7 @@ public static MapRequest.CompilationResult apply(WurstProjectConfigData projectC /** * Calculate a hash of the project configuration to detect changes */ - private static String calculateProjectConfigHash(WurstProjectConfigData projectConfig) { + private static String calculateProjectConfigHash(WurstProjectConfigData projectConfig, File buildDir) { try { // Serialize the relevant parts of the config StringBuilder sb = new StringBuilder(); @@ -178,6 +178,9 @@ private static String calculateProjectConfigHash(WurstProjectConfigData projectC .append(",").append(flags.getShowWavesOnCliffShores()) .append(",").append(flags.getShowWavesOnRollingShores()) .append("\n"); + WurstBuildConfig buildConfig = buildConfigFromBuildDir(buildDir); + sb.append("scriptMode:").append(buildConfig.scriptMode()).append("\n"); + sb.append("wc3Patch:").append(buildConfig.wc3Patch()).append("\n"); return ImportFile.calculateHash(sb.toString().getBytes(StandardCharsets.UTF_8)); } catch (Exception e) { @@ -200,9 +203,9 @@ private static void applyBuildMapData(WurstProjectConfigData projectConfig, File if (w3data.getWc3PatchVersion().isPresent()) { w3I.injectConfigsInJassScript(inputStream, sw, w3data.getWc3PatchVersion().get()); } else { - GameVersion version = GameVersion.VERSION_1_32; + GameVersion version = buildConfigFromBuildDir(buildDir).fallbackGameVersion(); WLogger.info( - "Failed to determine installed game version. Falling back to " + version + "Failed to determine installed game version. Falling back to wurst.build patch target: " + version ); w3I.injectConfigsInJassScript(inputStream, sw, version); } @@ -212,6 +215,14 @@ private static void applyBuildMapData(WurstProjectConfigData projectConfig, File } } + private static WurstBuildConfig buildConfigFromBuildDir(File buildDir) { + java.nio.file.Path projectRoot = buildDir.toPath().getParent(); + if (projectRoot == null) { + projectRoot = java.nio.file.Path.of("."); + } + return WurstBuildConfig.fromBuildFile(projectRoot.resolve(FILE_NAME)); + } + private static void prepareW3I(WurstProjectConfigData projectConfig, W3I w3I) { WurstProjectBuildMapData buildMapData = projectConfig.getBuildMapData(); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/WurstBuildConfig.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/WurstBuildConfig.java new file mode 100644 index 000000000..b5c0772fb --- /dev/null +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/WurstBuildConfig.java @@ -0,0 +1,206 @@ +package de.peeeq.wurstio.languageserver; + +import config.WurstProjectConfigData; +import de.peeeq.wurstscript.WLogger; +import net.moonlightflower.wc3libs.port.GameVersion; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Optional; + +import static de.peeeq.wurstio.languageserver.ProjectConfigBuilder.FILE_NAME; + +public final class WurstBuildConfig { + + public enum ScriptMode { + LUA, + JASS + } + + public enum Wc3Patch { + REFORGED, + PRE_129 + } + + private final Optional scriptMode; + private final Optional wc3Patch; + + private WurstBuildConfig(Optional scriptMode, Optional wc3Patch) { + this.scriptMode = scriptMode; + this.wc3Patch = wc3Patch; + } + + public static WurstBuildConfig empty() { + return new WurstBuildConfig(Optional.empty(), Optional.empty()); + } + + public static WurstBuildConfig fromWorkspaceRoot(WFile workspaceRoot) { + return fromBuildFile(Path.of(workspaceRoot.toString(), FILE_NAME)); + } + + public static WurstBuildConfig fromProject(WurstProjectConfigData projectConfig, WFile workspaceRoot) { + WurstBuildConfig fileConfig = fromWorkspaceRoot(workspaceRoot); + Optional scriptMode = readEnumGetter(projectConfig, "getScriptMode", ScriptMode::valueOf) + .or(fileConfig::scriptMode); + Optional wc3Patch = readEnumGetter(projectConfig, "getWc3Patch", WurstBuildConfig::parsePatchName) + .or(fileConfig::wc3Patch); + return new WurstBuildConfig(scriptMode, wc3Patch); + } + + static WurstBuildConfig fromBuildFile(Path buildFile) { + if (!Files.exists(buildFile)) { + return empty(); + } + Optional scriptMode = Optional.empty(); + Optional wc3Patch = Optional.empty(); + try { + for (String rawLine : Files.readAllLines(buildFile)) { + String line = stripComment(rawLine).trim(); + if (line.isEmpty() || Character.isWhitespace(rawLine.charAt(0))) { + continue; + } + int colon = line.indexOf(':'); + if (colon < 0) { + continue; + } + String key = line.substring(0, colon).trim(); + String value = normalizeScalar(line.substring(colon + 1).trim()); + if (key.equals("scriptMode")) { + scriptMode = parseScriptMode(value); + } else if (key.equals("wc3Patch")) { + wc3Patch = parsePatch(value); + } + } + } catch (IOException e) { + WLogger.warning("Could not read " + buildFile + " for build settings", e); + } + return new WurstBuildConfig(scriptMode, wc3Patch); + } + + public Optional scriptMode() { + return scriptMode; + } + + public Optional wc3Patch() { + return wc3Patch; + } + + public Wc3Patch wc3PatchOrReforged() { + return wc3Patch.orElse(Wc3Patch.REFORGED); + } + + public GameVersion fallbackGameVersion() { + if (wc3PatchOrReforged() == Wc3Patch.PRE_129) { + return new GameVersion("1.28"); + } + return GameVersion.VERSION_1_32; + } + + public List applyToCompileArgs(List compileArgs) { + if (!scriptMode.isPresent()) { + return compileArgs; + } + List result = new ArrayList<>(); + for (String arg : compileArgs) { + if (!"-lua".equals(arg)) { + result.add(arg); + } + } + if (scriptMode.get() == ScriptMode.LUA) { + result.add("-lua"); + } + return result; + } + + public boolean shouldUseReforgedLaunchArgs(Optional detectedVersion) { + return detectedVersion + .map(version -> version.compareTo(GameVersion.VERSION_1_32) >= 0) + .orElse(wc3PatchOrReforged() == Wc3Patch.REFORGED); + } + + public boolean shouldUseClassicWindowArg(Optional detectedVersion) { + return detectedVersion + .map(version -> version.compareTo(GameVersion.VERSION_1_31) < 0) + .orElse(wc3PatchOrReforged() == Wc3Patch.PRE_129); + } + + public boolean shouldCopyRunMapToWarcraftMapDir(Optional detectedVersion) { + return detectedVersion + .map(version -> version.compareTo(GameVersion.VERSION_1_32) < 0) + .orElse(wc3PatchOrReforged() == Wc3Patch.PRE_129); + } + + public boolean shouldUseInstallDirForMaps(Optional detectedVersion) { + return detectedVersion + .map(version -> version.compareTo(new GameVersion("1.27.9")) <= 0) + .orElse(wc3PatchOrReforged() == Wc3Patch.PRE_129); + } + + private static String stripComment(String line) { + int commentStart = line.indexOf('#'); + return commentStart >= 0 ? line.substring(0, commentStart) : line; + } + + private static String normalizeScalar(String value) { + String result = value; + if ((result.startsWith("\"") && result.endsWith("\"")) + || (result.startsWith("'") && result.endsWith("'"))) { + result = result.substring(1, result.length() - 1); + } + return result.trim(); + } + + private static Optional parseScriptMode(String value) { + String normalized = value.toUpperCase(Locale.ROOT); + try { + return Optional.of(ScriptMode.valueOf(normalized)); + } catch (IllegalArgumentException e) { + WLogger.warning("Ignoring unknown scriptMode in wurst.build: " + value); + return Optional.empty(); + } + } + + private static Optional parsePatch(String value) { + try { + return Optional.of(parsePatchName(value)); + } catch (IllegalArgumentException e) { + WLogger.warning("Ignoring unknown wc3Patch in wurst.build: " + value); + return Optional.empty(); + } + } + + private static Wc3Patch parsePatchName(String value) { + String normalized = value.toUpperCase(Locale.ROOT) + .replace(".", "_") + .replace("-", "_"); + if (normalized.equals("PRE1_29")) { + normalized = "PRE_129"; + } + return Wc3Patch.valueOf(normalized); + } + + private interface EnumParser { + T parse(String value); + } + + private static Optional readEnumGetter(WurstProjectConfigData projectConfig, String getterName, EnumParser parser) { + try { + Method getter = projectConfig.getClass().getMethod(getterName); + Object value = getter.invoke(projectConfig); + if (value == null) { + return Optional.empty(); + } + return Optional.of(parser.parse(value.toString())); + } catch (NoSuchMethodException ignored) { + return Optional.empty(); + } catch (Exception e) { + WLogger.warning("Could not read " + getterName + " from wurst.build config", e); + return Optional.empty(); + } + } +} diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/WurstCommands.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/WurstCommands.java index f271626a4..d2f6f2ec9 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/WurstCommands.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/WurstCommands.java @@ -125,16 +125,19 @@ public static List getCompileArgs(WFile rootPath, String... additionalAr Path configFile = Paths.get(rootPath.toString(), "wurst_run.args"); if (Files.exists(configFile)) { try (Stream lines = Files.lines(configFile)) { - return Stream.concat( + List args = Stream.concat( lines.filter(s -> s.startsWith("-")), Stream.of(additionalArgs) ).collect(Collectors.toList()); + return WurstBuildConfig.fromWorkspaceRoot(rootPath).applyToCompileArgs(args); } } else { String cfg = String.join("\n", defaultArgs) + "\n"; Files.write(configFile, cfg.getBytes(Charsets.UTF_8)); - return defaultArgs; + return WurstBuildConfig.fromWorkspaceRoot(rootPath).applyToCompileArgs( + Stream.concat(defaultArgs.stream(), Stream.of(additionalArgs)).collect(Collectors.toList()) + ); } } catch (IOException e) { throw new RuntimeException("Could not access wurst_run.args config file", e); diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/MapRequest.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/MapRequest.java index 8c15100bc..eb9025427 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/MapRequest.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/MapRequest.java @@ -924,7 +924,8 @@ private W3InstallationData getBestW3InstallationData() throws RequestFailedExcep W3InstallationData w3data = new W3InstallationData(langServer, new File(wc3Path.get()), this instanceof RunMap && !runArgs.isHotReload()); if (w3data.getWc3PatchVersion().isEmpty()) { - throw new RequestFailedException(MessageType.Error, "Could not find Warcraft III installation at specified path: " + wc3Path); + WLogger.warning("Could not determine Warcraft III version at specified path: " + wc3Path + + ". Falling back to wurst.build patch target."); } return w3data; diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/RunMap.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/RunMap.java index e95af6cd1..6f604af08 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/RunMap.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/RunMap.java @@ -5,6 +5,7 @@ import config.WurstProjectConfig; import config.WurstProjectConfigData; import de.peeeq.wurstio.gui.WurstGuiImpl; +import de.peeeq.wurstio.languageserver.WurstBuildConfig; import de.peeeq.wurstio.languageserver.ModelManager; import de.peeeq.wurstio.languageserver.WFile; import de.peeeq.wurstio.languageserver.WurstLanguageServer; @@ -33,8 +34,6 @@ import java.util.Optional; import static de.peeeq.wurstio.languageserver.ProjectConfigBuilder.FILE_NAME; -import static net.moonlightflower.wc3libs.port.GameVersion.VERSION_1_31; -import static net.moonlightflower.wc3libs.port.GameVersion.VERSION_1_32; /** * Created by peter on 16.05.16. @@ -42,6 +41,7 @@ public class RunMap extends MapRequest { private @Nullable File customTarget = null; + private @Nullable WurstBuildConfig buildConfig = null; public RunMap(WurstLanguageServer langServer, WFile workspaceRoot, Optional wc3Path, Optional map, @@ -68,6 +68,7 @@ public Object execute(ModelManager modelManager) throws IOException { throw new RequestFailedException(MessageType.Error, FILE_NAME + " file doesn't exist or is invalid. " + "Please install your project using grill or the wurst setup tool."); } + buildConfig = WurstBuildConfig.fromProject(projectConfig, workspaceRoot); // TODO use normal compiler for this, avoid code duplication WurstGui gui = new WurstGuiImpl(getWorkspaceAbsolute()); @@ -139,11 +140,10 @@ private void startGame(WurstGui gui, CompilationResult result) throws Exception gui.sendProgress("Starting Warcraft 3..."); File mapCopy = cachedMapFile.get(); - if (w3data.getWc3PatchVersion().isPresent()) { - GameVersion gameVersion = w3data.getWc3PatchVersion().get(); - if (gameVersion.compareTo(GameVersion.VERSION_1_32) < 0) { - mapCopy = copyToWarcraftMapDir(cachedMapFile.get()); - } + WurstBuildConfig buildConfig = getBuildConfig(); + Optional detectedGameVersion = w3data.getWc3PatchVersion(); + if (buildConfig.shouldCopyRunMapToWarcraftMapDir(detectedGameVersion)) { + mapCopy = copyToWarcraftMapDir(cachedMapFile.get()); } @@ -158,17 +158,15 @@ private void startGame(WurstGui gui, CompilationResult result) throws Exception if (!path.isEmpty()) { // now start the map - File gameExe = w3data.getGameExe().get(); - if (!w3data.getWc3PatchVersion().isPresent()) { - throw new RequestFailedException(MessageType.Error, wc3Path + " does not exist."); - } + File gameExe = w3data.getGameExe() + .orElseThrow(() -> new RequestFailedException(MessageType.Error, wc3Path + " does not exist.")); List cmd = Lists.newArrayList(gameExe.getAbsolutePath()); Optional wc3RunArgs = langServer.getConfigProvider().getWc3RunArgs(); if (!wc3RunArgs.isPresent() || StringUtils.isBlank(wc3RunArgs.get())) { - if (w3data.getWc3PatchVersion().get().compareTo(VERSION_1_32) >= 0) { + if (buildConfig.shouldUseReforgedLaunchArgs(detectedGameVersion)) { cmd.add("-launch"); } - if (w3data.getWc3PatchVersion().get().compareTo(VERSION_1_31) < 0) { + if (buildConfig.shouldUseClassicWindowArg(detectedGameVersion)) { cmd.add("-window"); } else { cmd.add("-windowmode"); @@ -327,7 +325,7 @@ private Optional findMapDocumentPath(String testMapName, File myDocument } } - if (w3data.getWc3PatchVersion().get().compareTo(new GameVersion("1.27.9")) <= 0) { + if (getBuildConfig().shouldUseInstallDirForMaps(w3data.getWc3PatchVersion())) { // 1.27 and lower compat WLogger.info("Version 1.27 or lower detected, changing file location"); documentPath = wc3Path; @@ -344,5 +342,11 @@ private Optional findMapDocumentPath(String testMapName, File myDocument return documentPath; } + private WurstBuildConfig getBuildConfig() { + if (buildConfig == null) { + buildConfig = WurstBuildConfig.fromWorkspaceRoot(workspaceRoot); + } + return buildConfig; + } } diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/utils/W3InstallationData.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/utils/W3InstallationData.java index 83e8d52b8..b85636f6c 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/utils/W3InstallationData.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/utils/W3InstallationData.java @@ -38,7 +38,7 @@ public W3InstallationData(Optional gameExe, Optional version) try { this.version = Optional.ofNullable(GameExe.getVersion(this.gameExe.get())); WLogger.info("Parsed game version from configured executable: " + this.version); - } catch (IOException e) { + } catch (IOException | RuntimeException e) { WLogger.warning("Could not parse game version from configured executable", e); } } @@ -64,7 +64,7 @@ public W3InstallationData(WurstLanguageServer languageServer, File wc3Path, bool loadFromPath(wc3Path); } - if (!gameExe.isPresent() || !version.isPresent()) { + if (!gameExe.isPresent()) { WLogger.warning("The provided wc3 path wasn't suitable. Falling back to discovery."); discoverExePath(); discoverVersion(); @@ -94,8 +94,8 @@ private void loadFromPath(File wc3Path) { version = gameExe.flatMap(exe -> { try { return Optional.ofNullable(GameExe.getVersion(exe)); - } catch (IOException e) { - WLogger.severe(e); + } catch (IOException | RuntimeException e) { + WLogger.warning("Could not parse game version from executable", e); } return Optional.empty(); diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstBuildConfigTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstBuildConfigTests.java new file mode 100644 index 000000000..1319fa70d --- /dev/null +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstBuildConfigTests.java @@ -0,0 +1,54 @@ +package tests.wurstscript.tests; + +import de.peeeq.wurstio.languageserver.WFile; +import de.peeeq.wurstio.languageserver.WurstBuildConfig; +import de.peeeq.wurstio.languageserver.WurstCommands; +import net.moonlightflower.wc3libs.port.GameVersion; +import org.testng.annotations.Test; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Optional; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +public class WurstBuildConfigTests { + + @Test + public void readsScriptModeAndPatchFromWurstBuild() throws Exception { + Path project = Files.createTempDirectory("wurst-build-config"); + Files.writeString(project.resolve("wurst.build"), """ + projectName: Test + dependencies: + - https://github.com/wurstscript/wurstStdlib2:pre1.29 + scriptMode: lua + wc3Patch: pre1.29 + """); + + WurstBuildConfig config = WurstBuildConfig.fromWorkspaceRoot(WFile.create(project.toFile())); + + assertEquals(config.scriptMode().orElseThrow(), WurstBuildConfig.ScriptMode.LUA); + assertEquals(config.wc3Patch().orElseThrow(), WurstBuildConfig.Wc3Patch.PRE_129); + assertEquals(config.fallbackGameVersion(), new GameVersion("1.28")); + assertTrue(config.shouldUseClassicWindowArg(Optional.empty())); + assertFalse(config.shouldUseReforgedLaunchArgs(Optional.empty())); + } + + @Test + public void compileArgsFollowConfiguredScriptMode() throws Exception { + Path project = Files.createTempDirectory("wurst-build-config-args"); + Files.writeString(project.resolve("wurst.build"), """ + projectName: Test + scriptMode: jass + """); + Files.writeString(project.resolve("wurst_run.args"), "-runcompiletimefunctions\n-lua\n"); + + List args = WurstCommands.getCompileArgs(WFile.create(project.toFile())); + + assertTrue(args.contains("-runcompiletimefunctions")); + assertFalse(args.contains("-lua")); + } +} From b08bff481ad880b085a12b23de910d576287e198 Mon Sep 17 00:00:00 2001 From: Frotty Date: Wed, 27 May 2026 15:23:30 +0200 Subject: [PATCH 2/3] one more cleanup --- .../languageserver/requests/MapRequest.java | 15 +++++++- .../tests/HotReloadPipelineTests.java | 38 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/MapRequest.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/MapRequest.java index eb9025427..9259ee486 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/MapRequest.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/MapRequest.java @@ -808,7 +808,7 @@ protected File loadMapScript(Optional mapCopy, ModelManager modelManager, "RunArg noExtractMapScript is set but no war3map.j is provided inside the wurst folder"); } } - if (MapRequest.mapLastModified > lastMapModified || !MapRequest.mapPath.equals(lastMapPath)) { + if (shouldExtractMapScript(scriptFile)) { lastMapModified = MapRequest.mapLastModified; lastMapPath = MapRequest.mapPath; System.out.println("Map not cached yet, extracting script"); @@ -851,6 +851,19 @@ protected File loadMapScript(Optional mapCopy, ModelManager modelManager, return scriptFile; } + private static boolean shouldExtractMapScript(File scriptFile) { + if (!scriptFile.exists()) { + return true; + } + if (!MapRequest.mapPath.equals(lastMapPath)) { + return true; + } + if (MapRequest.mapLastModified > lastMapModified) { + return true; + } + return MapRequest.mapLastModified > scriptFile.lastModified(); + } + private static void ensureScriptIsSynced(ModelManager modelManager, File scriptFile) { CompilationUnit compilationUnit = modelManager.getCompilationUnit(WFile.create(scriptFile)); if (compilationUnit == null) { diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/HotReloadPipelineTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/HotReloadPipelineTests.java index ef16a0a70..7bcb20e3a 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/HotReloadPipelineTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/HotReloadPipelineTests.java @@ -62,6 +62,44 @@ public void hotReloadExtractionUsesSourceMap() throws Exception { assertEquals(Files.readString(scriptFile.toPath()), "source script"); } + @Test + public void mapScriptIsExtractedWhenWorkspaceScriptIsMissingDespiteStaticCacheHit() throws Exception { + File projectFolder = new File("./temp/testProject_extract_missing_script/"); + File wurstFolder = new File(projectFolder, "wurst"); + newCleanFolder(wurstFolder); + + File sourceMap = new File(projectFolder, "source_map.w3x"); + Files.write(sourceMap.toPath(), new byte[] {0x01}); + + WurstLanguageServer langServer = new WurstLanguageServer(); + TestMapRequest request = new TestMapRequest( + langServer, + Optional.of(sourceMap), + List.of(), + WFile.create(projectFolder), + Map.of(sourceMap, "fresh map script".getBytes(StandardCharsets.UTF_8)) + ); + + MapRequest.mapLastModified = sourceMap.lastModified(); + MapRequest.mapPath = sourceMap.getAbsolutePath(); + + File firstExtract = request.loadMapScriptForTest( + Optional.of(sourceMap), + new ModelManagerImpl(projectFolder, new BufferManager()), + new WurstGuiLogger() + ); + assertEquals(Files.readString(firstExtract.toPath()), "fresh map script"); + + Files.delete(firstExtract.toPath()); + File secondExtract = request.loadMapScriptForTest( + Optional.of(sourceMap), + new ModelManagerImpl(projectFolder, new BufferManager()), + new WurstGuiLogger() + ); + + assertEquals(Files.readString(secondExtract.toPath()), "fresh map script"); + } + @Test public void cachedMapFileNameIsModeSpecific() throws Exception { File projectFolder = new File("./temp/testProject_cache_mode_specific/"); From ffa9bb627fbd3e29c4c2f991c3ba11955f475dfc Mon Sep 17 00:00:00 2001 From: Frotty Date: Wed, 27 May 2026 15:27:48 +0200 Subject: [PATCH 3/3] review fix --- .../wurstio/languageserver/WurstBuildConfig.java | 5 ++--- .../wurstscript/tests/WurstBuildConfigTests.java | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/WurstBuildConfig.java b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/WurstBuildConfig.java index b5c0772fb..c76717c00 100644 --- a/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/WurstBuildConfig.java +++ b/de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/WurstBuildConfig.java @@ -136,9 +136,8 @@ public boolean shouldCopyRunMapToWarcraftMapDir(Optional detectedVe } public boolean shouldUseInstallDirForMaps(Optional detectedVersion) { - return detectedVersion - .map(version -> version.compareTo(new GameVersion("1.27.9")) <= 0) - .orElse(wc3PatchOrReforged() == Wc3Patch.PRE_129); + return detectedVersion.orElseGet(this::fallbackGameVersion) + .compareTo(new GameVersion("1.27.9")) <= 0; } private static String stripComment(String line) { diff --git a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstBuildConfigTests.java b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstBuildConfigTests.java index 1319fa70d..0c6d0588f 100644 --- a/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstBuildConfigTests.java +++ b/de.peeeq.wurstscript/src/test/java/tests/wurstscript/tests/WurstBuildConfigTests.java @@ -35,6 +35,22 @@ public void readsScriptModeAndPatchFromWurstBuild() throws Exception { assertEquals(config.fallbackGameVersion(), new GameVersion("1.28")); assertTrue(config.shouldUseClassicWindowArg(Optional.empty())); assertFalse(config.shouldUseReforgedLaunchArgs(Optional.empty())); + assertFalse(config.shouldUseInstallDirForMaps(Optional.empty())); + } + + @Test + public void installDirMapPathIsOnlyForDetected127OrLower() throws Exception { + Path project = Files.createTempDirectory("wurst-build-config-map-path"); + Files.writeString(project.resolve("wurst.build"), """ + projectName: Test + wc3Patch: pre1.29 + """); + + WurstBuildConfig config = WurstBuildConfig.fromWorkspaceRoot(WFile.create(project.toFile())); + + assertTrue(config.shouldUseInstallDirForMaps(Optional.of(new GameVersion("1.27")))); + assertFalse(config.shouldUseInstallDirForMaps(Optional.of(new GameVersion("1.28")))); + assertFalse(config.shouldUseInstallDirForMaps(Optional.empty())); } @Test