77import de .peeeq .wurstio .gui .WurstGuiImpl ;
88import de .peeeq .wurstio .languageserver .ModelManager ;
99import de .peeeq .wurstio .languageserver .WFile ;
10+ import de .peeeq .wurstio .languageserver .WurstBuildConfig ;
1011import de .peeeq .wurstio .languageserver .WurstLanguageServer ;
12+ import de .peeeq .wurstio .utils .W3InstallationData ;
1113import de .peeeq .wurstscript .WLogger ;
1214import de .peeeq .wurstscript .attributes .CompileError ;
1315import de .peeeq .wurstscript .gui .WurstGui ;
2224
2325import javax .swing .*;
2426import javax .swing .filechooser .FileSystemView ;
27+ import java .awt .GraphicsEnvironment ;
2528import java .io .File ;
2629import java .io .FileNotFoundException ;
2730import java .io .IOException ;
@@ -136,7 +139,11 @@ private void startGame(WurstGui gui, CompilationResult result) throws Exception
136139 gui .sendProgress ("Starting Warcraft 3..." );
137140
138141 File mapCopy = cachedMapFile .get ();
139- Optional <GameVersion > detectedGameVersion = w3data .getWc3PatchVersion ();
142+ W3InstallationData launchData = resolveLaunchData ();
143+ if (launchData == null ) {
144+ throw new RequestFailedException (MessageType .Info , "Run canceled." );
145+ }
146+ Optional <GameVersion > detectedGameVersion = launchData .getWc3PatchVersion ();
140147 if (buildConfig .shouldCopyRunMapToWarcraftMapDir (detectedGameVersion )) {
141148 mapCopy = copyToWarcraftMapDir (cachedMapFile .get ());
142149 }
@@ -153,7 +160,7 @@ private void startGame(WurstGui gui, CompilationResult result) throws Exception
153160
154161 if (!path .isEmpty ()) {
155162 // now start the map
156- File gameExe = w3data .getGameExe ()
163+ File gameExe = launchData .getGameExe ()
157164 .orElseThrow (() -> new RequestFailedException (MessageType .Error , wc3Path + " does not exist." ));
158165 List <String > cmd = Lists .newArrayList (gameExe .getAbsolutePath ());
159166 Optional <String > wc3RunArgs = langServer .getConfigProvider ().getWc3RunArgs ();
@@ -186,6 +193,104 @@ private void startGame(WurstGui gui, CompilationResult result) throws Exception
186193 }
187194 }
188195
196+ private W3InstallationData resolveLaunchData () {
197+ W3InstallationData launchData = w3data ;
198+ while (shouldWarnClientPatchMismatch (launchData )) {
199+ String projectTarget = buildConfig .wc3PatchName ().orElse ("configured patch" );
200+ String clientTarget = launchData .getWc3PatchVersion ()
201+ .map (RunMap ::describeClientVersion )
202+ .orElse ("unknown Warcraft III version" );
203+ String message = "This project targets " + projectTarget + ", but the selected Warcraft III client looks like "
204+ + clientTarget + ". The map may not start correctly." ;
205+ WLogger .warning (message );
206+
207+ MismatchChoice choice = chooseMismatchAction (message );
208+ if (choice == MismatchChoice .CANCEL ) {
209+ return null ;
210+ }
211+ if (choice == MismatchChoice .CONTINUE ) {
212+ return launchData ;
213+ }
214+ Optional <W3InstallationData > selected = chooseAlternateGamePath ();
215+ if (selected .isEmpty ()) {
216+ return null ;
217+ }
218+ launchData = selected .get ();
219+ }
220+ if (buildConfig .wc3Patch ().isPresent () && launchData .getWc3PatchVersion ().isEmpty ()) {
221+ WLogger .warning ("Could not determine Warcraft III client version. If the map does not start, select a matching Warcraft III installation." );
222+ }
223+ return launchData ;
224+ }
225+
226+ private boolean shouldWarnClientPatchMismatch (W3InstallationData launchData ) {
227+ Optional <WurstBuildConfig .Wc3Patch > projectKind = buildConfig .wc3Patch ();
228+ Optional <GameVersion > clientVersion = launchData .getWc3PatchVersion ();
229+ if (projectKind .isEmpty () || clientVersion .isEmpty ()) {
230+ return false ;
231+ }
232+ return projectKind .get () != kindForVersion (clientVersion .get ());
233+ }
234+
235+ private static WurstBuildConfig .Wc3Patch kindForVersion (GameVersion version ) {
236+ if (version .compareTo (new GameVersion ("1.29" )) < 0 ) {
237+ return WurstBuildConfig .Wc3Patch .PRE_129 ;
238+ }
239+ if (version .compareTo (GameVersion .VERSION_1_32 ) < 0 ) {
240+ return WurstBuildConfig .Wc3Patch .CLASSIC ;
241+ }
242+ return WurstBuildConfig .Wc3Patch .REFORGED ;
243+ }
244+
245+ private static String describeClientVersion (GameVersion version ) {
246+ return kindForVersion (version ) + " (" + version + ")" ;
247+ }
248+
249+ private MismatchChoice chooseMismatchAction (String message ) {
250+ if (GraphicsEnvironment .isHeadless ()) {
251+ return MismatchChoice .CONTINUE ;
252+ }
253+ Object [] options = {"Continue" , "Choose Warcraft III folder" , "Cancel" };
254+ int result = JOptionPane .showOptionDialog (
255+ null ,
256+ message ,
257+ "Warcraft III version mismatch" ,
258+ JOptionPane .DEFAULT_OPTION ,
259+ JOptionPane .WARNING_MESSAGE ,
260+ null ,
261+ options ,
262+ options [1 ]
263+ );
264+ if (result == 1 ) {
265+ return MismatchChoice .CHOOSE_OTHER ;
266+ }
267+ if (result == 2 || result == JOptionPane .CLOSED_OPTION ) {
268+ return MismatchChoice .CANCEL ;
269+ }
270+ return MismatchChoice .CONTINUE ;
271+ }
272+
273+ private Optional <W3InstallationData > chooseAlternateGamePath () {
274+ if (GraphicsEnvironment .isHeadless ()) {
275+ return Optional .empty ();
276+ }
277+ JFileChooser fileChooser = new JFileChooser (FileSystemView .getFileSystemView ().getHomeDirectory ());
278+ fileChooser .setFileSelectionMode (JFileChooser .DIRECTORIES_ONLY );
279+ fileChooser .setDialogTitle ("Select Warcraft III installation folder" );
280+ int result = fileChooser .showOpenDialog (null );
281+ if (result != JFileChooser .APPROVE_OPTION ) {
282+ return Optional .empty ();
283+ }
284+ File selectedFolder = fileChooser .getSelectedFile ();
285+ return Optional .of (new W3InstallationData (langServer , selectedFolder , true , Optional .empty ()));
286+ }
287+
288+ private enum MismatchChoice {
289+ CONTINUE ,
290+ CHOOSE_OTHER ,
291+ CANCEL
292+ }
293+
189294
190295 private void callJhcrUpdate (File mapScript ) throws IOException , InterruptedException {
191296 File mapScriptFolder = mapScript .getParentFile ();
0 commit comments