3838import org .eclipse .lsp4j .services .LanguageClient ;
3939import org .jetbrains .annotations .Nullable ;
4040
41+ import javax .swing .*;
4142import java .io .File ;
4243import java .io .FileNotFoundException ;
4344import java .io .IOException ;
45+ import java .nio .channels .FileChannel ;
4446import java .nio .channels .NonWritableChannelException ;
4547import java .nio .charset .StandardCharsets ;
4648import 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 <>();
0 commit comments