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
Original file line number Diff line number Diff line change
Expand Up @@ -443,23 +443,26 @@ private Set<String> 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();
}
}

Expand All @@ -472,6 +475,26 @@ private CompilationUnit compileFromJar(WurstGui gui, String filename) throws IOE
}
}

private Optional<File> 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");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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) {
Expand All @@ -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);
}
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
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> scriptMode;
private final Optional<Wc3Patch> wc3Patch;

private WurstBuildConfig(Optional<ScriptMode> scriptMode, Optional<Wc3Patch> 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> scriptMode = readEnumGetter(projectConfig, "getScriptMode", ScriptMode::valueOf)
.or(fileConfig::scriptMode);
Optional<Wc3Patch> 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> scriptMode = Optional.empty();
Optional<Wc3Patch> 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> scriptMode() {
return scriptMode;
}

public Optional<Wc3Patch> 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<String> applyToCompileArgs(List<String> compileArgs) {
if (!scriptMode.isPresent()) {
return compileArgs;
}
List<String> 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<GameVersion> detectedVersion) {
return detectedVersion
.map(version -> version.compareTo(GameVersion.VERSION_1_32) >= 0)
.orElse(wc3PatchOrReforged() == Wc3Patch.REFORGED);
}

public boolean shouldUseClassicWindowArg(Optional<GameVersion> detectedVersion) {
return detectedVersion
.map(version -> version.compareTo(GameVersion.VERSION_1_31) < 0)
.orElse(wc3PatchOrReforged() == Wc3Patch.PRE_129);
}

public boolean shouldCopyRunMapToWarcraftMapDir(Optional<GameVersion> detectedVersion) {
return detectedVersion
.map(version -> version.compareTo(GameVersion.VERSION_1_32) < 0)
.orElse(wc3PatchOrReforged() == Wc3Patch.PRE_129);
}

public boolean shouldUseInstallDirForMaps(Optional<GameVersion> detectedVersion) {
return detectedVersion.orElseGet(this::fallbackGameVersion)
.compareTo(new GameVersion("1.27.9")) <= 0;
}

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<ScriptMode> 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<Wc3Patch> 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> {
T parse(String value);
}

private static <T> Optional<T> readEnumGetter(WurstProjectConfigData projectConfig, String getterName, EnumParser<T> 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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,19 @@ public static List<String> getCompileArgs(WFile rootPath, String... additionalAr
Path configFile = Paths.get(rootPath.toString(), "wurst_run.args");
if (Files.exists(configFile)) {
try (Stream<String> lines = Files.lines(configFile)) {
return Stream.concat(
List<String> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -808,7 +808,7 @@ protected File loadMapScript(Optional<File> 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");
Expand Down Expand Up @@ -851,6 +851,19 @@ protected File loadMapScript(Optional<File> 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) {
Expand Down Expand Up @@ -924,7 +937,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;
Expand Down
Loading
Loading