Skip to content

Commit dd91877

Browse files
committed
better runmap file in use handling
1 parent e3eaa6c commit dd91877

3 files changed

Lines changed: 106 additions & 14 deletions

File tree

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/BuildMap.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,28 @@ public Object execute(ModelManager modelManager) throws IOException {
6161
// first we copy in same location to ensure validity
6262
File buildDir = getBuildDir();
6363
String fileName = projectConfig.getBuildMapData().getFileName();
64-
Optional<File> targetMap = Optional.of(
65-
new File(buildDir, fileName.isEmpty() ? projectConfig.getProjectName() + ".w3x" : fileName + ".w3x"));
66-
CompilationResult result = compileScript(modelManager, gui, targetMap, projectConfig, buildDir, true);
67-
68-
injectMapData(gui, targetMap, result);
69-
70-
Files.copy(getCachedMapFile().toPath(), targetMap.get().toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
64+
File targetMapFile = new File(buildDir, fileName.isEmpty() ? projectConfig.getProjectName() + ".w3x" : fileName + ".w3x");
65+
targetMapFile = ensureWritableTargetFile(
66+
targetMapFile,
67+
"Build Map",
68+
"The output map file is in use and cannot be replaced.\nClose Warcraft III and click Retry, choose Rename to use a temporary file name, or Cancel.",
69+
"Build canceled because output map target is in use."
70+
);
71+
CompilationResult result = compileScript(modelManager, gui, Optional.of(targetMapFile), projectConfig, buildDir, true);
72+
73+
injectMapData(gui, Optional.of(targetMapFile), result);
74+
75+
targetMapFile = ensureWritableTargetFile(
76+
targetMapFile,
77+
"Build Map",
78+
"The output map file is still in use and cannot be replaced.\nClick Retry, choose Rename to use a temporary file name, or Cancel.",
79+
"Build canceled because output map target is in use."
80+
);
81+
Files.copy(getCachedMapFile().toPath(), targetMapFile.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);
7182

7283
gui.sendProgress("Finalizing map");
7384

74-
try (MpqEditor mpq = MpqEditorFactory.getEditor(targetMap)) {
85+
try (MpqEditor mpq = MpqEditorFactory.getEditor(Optional.of(targetMapFile))) {
7586
if (mpq != null) {
7687
mpq.closeWithCompression();
7788
}

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/MapRequest.java

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,11 @@
3838
import org.eclipse.lsp4j.services.LanguageClient;
3939
import org.jetbrains.annotations.Nullable;
4040

41+
import javax.swing.*;
4142
import java.io.File;
4243
import java.io.FileNotFoundException;
4344
import java.io.IOException;
45+
import java.nio.channels.FileChannel;
4446
import java.nio.channels.NonWritableChannelException;
4547
import java.nio.charset.StandardCharsets;
4648
import java.nio.file.Path;
@@ -67,6 +69,7 @@ public abstract class MapRequest extends UserRequest<Object> {
6769

6870
private static Long lastMapModified = 0L;
6971
private static String lastMapPath = "";
72+
protected String cachedMapFileName = "cached_map.w3x";
7073

7174
public static final String BUILD_CONFIGURED_SCRIPT_NAME = "01_war3mapj_with_config.j.txt";
7275
public static final String BUILD_COMPILED_JASS_NAME = "02_compiled.j.txt";
@@ -376,7 +379,69 @@ protected File getCachedMapFile() {
376379
if (!cacheDir.exists()) {
377380
UtilsIO.mkdirs(cacheDir);
378381
}
379-
return new File(cacheDir, "cached_map.w3x");
382+
return new File(cacheDir, cachedMapFileName);
383+
}
384+
385+
protected File ensureWritableTargetFile(File targetFile, String dialogTitle, String lockMessage,
386+
String cancelMessage) {
387+
File currentTarget = targetFile;
388+
while (isLocked(currentTarget)) {
389+
int choice = showTargetLockedDialog(dialogTitle, lockMessage);
390+
if (choice == 0 || choice == JOptionPane.CLOSED_OPTION) {
391+
throw new RequestFailedException(MessageType.Warning, cancelMessage);
392+
} else if (choice == 1) {
393+
continue;
394+
} else if (choice == 2) {
395+
currentTarget = createTemporaryTargetFile(currentTarget);
396+
}
397+
}
398+
return currentTarget;
399+
}
400+
401+
private int showTargetLockedDialog(String dialogTitle, String message) {
402+
JFrame frame = new JFrame();
403+
frame.setAlwaysOnTop(true);
404+
Object[] options = { "Cancel", "Retry", "Rename" };
405+
return JOptionPane.showOptionDialog(
406+
frame,
407+
message,
408+
dialogTitle,
409+
JOptionPane.DEFAULT_OPTION,
410+
JOptionPane.WARNING_MESSAGE,
411+
null,
412+
options,
413+
options[1]
414+
);
415+
}
416+
417+
private File createTemporaryTargetFile(File currentTarget) {
418+
String currentName = currentTarget.getName();
419+
String baseName = currentName.endsWith(".w3x")
420+
? currentName.substring(0, currentName.length() - 4)
421+
: currentName;
422+
try {
423+
File parent = currentTarget.getParentFile();
424+
if (parent == null) {
425+
parent = getBuildDir();
426+
}
427+
File tempTarget = java.nio.file.Files.createTempFile(parent.toPath(), baseName + "_", ".w3x").toFile();
428+
tempTarget.deleteOnExit();
429+
WLogger.info("Using temporary map target due to locked file: " + tempTarget.getAbsolutePath());
430+
return tempTarget;
431+
} catch (IOException e) {
432+
throw new RequestFailedException(MessageType.Error, "Could not create temporary map target file.", e);
433+
}
434+
}
435+
436+
private boolean isLocked(File targetMap) {
437+
if (!targetMap.exists()) {
438+
return false;
439+
}
440+
try (FileChannel ignored = FileChannel.open(targetMap.toPath(), StandardOpenOption.WRITE)) {
441+
return false;
442+
} catch (IOException e) {
443+
return true;
444+
}
380445
}
381446

382447
/**
@@ -541,10 +606,7 @@ protected File loadMapScript(Optional<File> mapCopy, ModelManager modelManager,
541606
System.out.println("No extract map script enabled - not extracting.");
542607
if (scriptFile.exists()) {
543608
System.out.println("war3map.j exists at wurst root.");
544-
CompilationUnit compilationUnit = modelManager.getCompilationUnit(WFile.create(scriptFile));
545-
if (compilationUnit == null) {
546-
modelManager.syncCompilationUnit(WFile.create(scriptFile));
547-
}
609+
ensureScriptIsSynced(modelManager, scriptFile);
548610
return scriptFile;
549611
} else {
550612
throw new CompileError(new WPos("", new LineOffsets(), 0, 0),
@@ -582,15 +644,25 @@ protected File loadMapScript(Optional<File> mapCopy, ModelManager modelManager,
582644
WLogger.info("new map, use extracted");
583645
// write mapfile from map to workspace
584646
Files.write(extractedScript, scriptFile);
647+
ensureScriptIsSynced(modelManager, scriptFile);
585648
}
586649
} else {
587650
System.out.println("Map not modified, not extracting script");
588651
}
589-
652+
if (scriptFile.exists()) {
653+
ensureScriptIsSynced(modelManager, scriptFile);
654+
}
590655

591656
return scriptFile;
592657
}
593658

659+
private static void ensureScriptIsSynced(ModelManager modelManager, File scriptFile) {
660+
CompilationUnit compilationUnit = modelManager.getCompilationUnit(WFile.create(scriptFile));
661+
if (compilationUnit == null) {
662+
modelManager.syncCompilationUnit(WFile.create(scriptFile));
663+
}
664+
}
665+
594666
protected CompilationResult applyProjectConfig(WurstGui gui, Optional<File> testMap, File buildDir, WurstProjectConfigData projectConfig, File scriptFile,
595667
String outputScriptName) {
596668
AtomicReference<CompilationResult> result = new AtomicReference<>();

de.peeeq.wurstscript/src/main/java/de/peeeq/wurstio/languageserver/requests/RunMap.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,15 @@ private String compileMap(ModelManager modelManager, WurstGui gui, WurstProjectC
102102
mapLastModified = map.get().lastModified();
103103
mapPath = map.get().getAbsolutePath();
104104
}
105+
if (!runArgs.isHotReload()) {
106+
File cacheTarget = ensureWritableTargetFile(
107+
getCachedMapFile(),
108+
"Run Map",
109+
"The cached run map file is in use and cannot be updated.\nClose Warcraft III and click Retry, choose Rename to use a temporary file name, or Cancel.",
110+
"Run canceled because the cached map file is in use."
111+
);
112+
cachedMapFileName = cacheTarget.getName();
113+
}
105114
Optional<File> testMap = map.map($ -> new File(buildDir, "WurstRunMap.w3x"));
106115
CompilationResult result = compileScript(modelManager, gui, testMap, projectConfig, buildDir, false);
107116

0 commit comments

Comments
 (0)