1717
1818from gui .widgets import CollapsiblePane
1919from utils .constants import *
20- from utils .helpers import format_size , get_folder_size , get_local_ip , get_server_port
21- from utils .api_client import fetch_player_avatar_image , fetch_player_uuid , download_server_jar , get_server_versions , fetch_username_from_uuid , get_forge_versions
20+ from utils .helpers import format_size , get_folder_size , get_local_ip , get_server_port , get_java_version , get_required_java_version
21+ from utils .api_client import fetch_player_avatar_image , fetch_player_uuid , download_server_jar , get_server_versions , fetch_username_from_uuid , get_forge_versions , download_jre
2222from server .server_handler import ServerHandler
2323from server .config_manager import ConfigManager
2424
@@ -328,7 +328,7 @@ def _create_setup_wizard(self):
328328 self .vanilla_version_frame = ctk .CTkFrame (self .version_options_container , fg_color = "transparent" )
329329 ctk .CTkLabel (self .vanilla_version_frame , text = "2. Select Minecraft Version" , font = ctk .CTkFont (weight = "bold" )).pack (anchor = 'w' , pady = (10 ,5 ))
330330 self .server_version_var = tk .StringVar ()
331- self .version_menu = ctk .CTkComboBox (self .vanilla_version_frame , variable = self .server_version_var , values = ["Loading..." ], state = "disabled" )
331+ self .version_menu = ctk .CTkComboBox (self .vanilla_version_frame , variable = self .server_version_var , values = ["Loading..." ], state = "disabled" , command = self . _check_java_compatibility )
332332 self .version_menu .pack (fill = tk .X , pady = 5 )
333333
334334 # Steps 2 & 3 (Forge): Forge-specific widgets
@@ -356,9 +356,20 @@ def _create_setup_wizard(self):
356356 self .server_name_var = tk .StringVar ()
357357 self .server_name_entry = ctk .CTkEntry (self .install_frame , textvariable = self .server_name_var )
358358 self .server_name_entry .pack (fill = tk .X , pady = 5 )
359+
360+ # --- Java Validation ---
361+ self .java_status_frame = ctk .CTkFrame (self .install_frame , fg_color = "gray20" )
362+ self .java_status_frame .pack (fill = tk .X , pady = 10 , ipady = 5 )
363+ self .java_status_label = ctk .CTkLabel (self .java_status_frame , text = "Java Status: Checking..." , anchor = "w" )
364+ self .java_status_label .pack (side = tk .LEFT , padx = 10 )
359365
366+ self .download_jre_var = tk .BooleanVar (value = False )
367+ self .download_jre_checkbox = ctk .CTkCheckBox (self .install_frame , text = "Automatically download a compatible Java runtime" , variable = self .download_jre_var )
368+ # Will be packed later if needed
369+
360370 self .eula_accepted_var = tk .BooleanVar (value = False )
361- ctk .CTkCheckBox (self .install_frame , text = "I agree to the Minecraft EULA (minecraft.net/eula)" , variable = self .eula_accepted_var ).pack (fill = tk .X , pady = 10 )
371+ self .eula_checkbox = ctk .CTkCheckBox (self .install_frame , text = "I agree to the Minecraft EULA (minecraft.net/eula)" , variable = self .eula_accepted_var )
372+ self .eula_checkbox .pack (fill = tk .X , pady = 10 )
362373
363374 # --- Widgets for "Use Existing Server" ---
364375 ctk .CTkLabel (self .existing_frame , text = "1. Choose Existing Server Location" , font = ctk .CTkFont (weight = "bold" )).pack (anchor = 'w' , pady = (10 ,5 ))
@@ -370,7 +381,7 @@ def _create_setup_wizard(self):
370381
371382 # --- Action Button & Progress ---
372383 self .action_button = ctk .CTkButton (setup_frame , text = "Download and Install Server" , command = self ._start_setup_action )
373- self .action_button .pack (pady = (30 , 10 ), ipady = 10 , fill = tk .X )
384+ self .action_button .pack (pady = (20 , 10 ), ipady = 10 , fill = tk .X )
374385 self .progress_bar = ctk .CTkProgressBar (setup_frame )
375386 self .progress_bar .set (0 )
376387 self .progress_bar .pack (fill = tk .X , pady = 5 )
@@ -383,6 +394,36 @@ def _create_setup_wizard(self):
383394 self ._toggle_setup_view ()
384395 self ._on_server_type_change ()
385396
397+ def _check_java_compatibility (self , * args ):
398+ mc_version_str = self .server_version_var .get ()
399+ if self .server_type_var .get ().lower () == 'forge' :
400+ mc_version_str = self .forge_mc_version_var .get ()
401+
402+ if not mc_version_str or "Loading" in mc_version_str or "Error" in mc_version_str :
403+ self .java_status_label .configure (text = "Java Status: Select a Minecraft version first" )
404+ return
405+
406+ required_version = get_required_java_version (mc_version_str )
407+ installed_version = get_java_version ()
408+
409+ if installed_version is None :
410+ status_text = f"❌ Java Not Found (Required: Java { required_version } +)"
411+ status_color = "red"
412+ self .download_jre_var .set (True )
413+ self .download_jre_checkbox .pack (fill = tk .X , pady = 5 , before = self .eula_checkbox )
414+ elif installed_version < required_version :
415+ status_text = f"⚠️ Wrong Version: Java { installed_version } found (Required: Java { required_version } +)"
416+ status_color = "orange"
417+ self .download_jre_var .set (True )
418+ self .download_jre_checkbox .pack (fill = tk .X , pady = 5 , before = self .eula_checkbox )
419+ else :
420+ status_text = f"✅ Java { installed_version } Found (Compatible)"
421+ status_color = "green"
422+ self .download_jre_var .set (False )
423+ self .download_jre_checkbox .pack_forget ()
424+
425+ self .java_status_label .configure (text = status_text , text_color = status_color )
426+
386427 def _on_server_type_change (self , * args ):
387428 server_type = self .server_type_var .get ().lower ()
388429
@@ -400,6 +441,8 @@ def _on_server_type_change(self, *args):
400441 self .location_step_label .configure (text = "3. Choose Parent Directory" )
401442 self .name_step_label .configure (text = "4. Name Server Folder" )
402443 self ._update_server_versions ()
444+
445+ self ._check_java_compatibility ()
403446
404447 def _update_forge_versions (self ):
405448 self .forge_mc_version_var .set ("Loading..." )
@@ -420,6 +463,7 @@ def update_ui():
420463 else :
421464 self .forge_mc_version_var .set ("Error" )
422465 self .forge_version_var .set ("Error" )
466+ self ._check_java_compatibility ()
423467
424468 if hasattr (self , 'master' ):
425469 self .master .after (0 , update_ui )
@@ -435,11 +479,13 @@ def _on_forge_mc_version_selected(self, mc_version):
435479 else :
436480 self .forge_version_menu .configure (values = [], state = "disabled" )
437481 self .forge_version_var .set ("N/A" )
482+ self ._check_java_compatibility ()
438483
439484 def _update_server_versions (self , * args ):
440485 server_type = self .server_type_var .get ()
441486 if server_type .lower () == "forge" :
442- return # No need to fetch versions for Forge this way
487+ self ._check_java_compatibility ()
488+ return
443489
444490 self .server_version_var .set ("Loading..." )
445491 self .version_menu .configure (state = tk .DISABLED )
@@ -455,6 +501,7 @@ def update_ui():
455501 self .server_version_var .set (versions [0 ])
456502 else :
457503 self .server_version_var .set ("Error fetching versions" )
504+ self ._check_java_compatibility ()
458505
459506 if hasattr (self , 'master' ):
460507 self .master .after (0 , update_ui )
@@ -580,6 +627,32 @@ def _detect_server_version(self, server_path):
580627
581628 def _perform_server_installation (self ):
582629 try :
630+ # --- Java Handling ---
631+ if self .download_jre_var .get ():
632+ mc_version_str = self .server_version_var .get ()
633+ if self .server_type_var .get ().lower () == 'forge' :
634+ mc_version_str = self .forge_mc_version_var .get ()
635+
636+ required_java = get_required_java_version (mc_version_str )
637+ self .master .after (0 , self .status_label .configure , {'text' : f"Downloading compatible Java JRE (Version { required_java } )..." })
638+
639+ def progress_callback (p ):
640+ self .master .after (0 , self .progress_bar .set , p / 100 )
641+
642+ jre_path = download_jre (java_version = required_java , progress_callback = progress_callback )
643+
644+ if not jre_path :
645+ raise Exception (f"Failed to download the required Java JRE. Please install Java { required_java } manually and try again." )
646+
647+ # On Windows, the executable is in /bin/java.exe
648+ java_exe_path = os .path .join (jre_path , 'bin' , 'java.exe' )
649+ if not os .path .exists (java_exe_path ):
650+ raise Exception (f"Could not find java.exe in the downloaded JRE folder." )
651+
652+ self .java_path_var .set (java_exe_path )
653+ self .master .after (0 , self .status_label .configure , {'text' : "Java JRE downloaded successfully." })
654+ self .master .after (0 , self .progress_bar .set , 0 )
655+
583656 server_type = self .server_type_var .get ().lower ()
584657 parent_dir = self .install_location_var .get ()
585658 server_folder_name = self .server_name_var .get ()
@@ -595,7 +668,7 @@ def _perform_server_installation(self):
595668 os .makedirs (install_path , exist_ok = True )
596669
597670 # Initialize ServerHandler here to use its methods
598- temp_server_handler = ServerHandler (install_path , server_type , "1" , "2" , "G" , lambda msg , level : self .master .after (0 , self .log_to_console , msg , level ))
671+ temp_server_handler = ServerHandler (install_path , server_type , "1" , "2" , "G" , lambda msg , level : self .master .after (0 , self .log_to_console , msg , level ), java_path = self . java_path_var . get () )
599672
600673 if server_type == 'forge' :
601674 forge_version = self .forge_version_var .get ()
@@ -627,7 +700,7 @@ def install_logic():
627700 self .master .after (0 , self .status_label .configure , {'text' : "First-time server run to generate files..." })
628701 self .master .after (0 , self .progress_bar .set , 0 )
629702
630- initial_run_command = ['java' , '-jar' , jar_name , '--nogui' ]
703+ initial_run_command = [self . java_path_var . get () , '-jar' , jar_name , '--nogui' ]
631704 process = subprocess .Popen (initial_run_command , cwd = install_path , stdout = subprocess .PIPE , stderr = subprocess .PIPE , text = True , creationflags = subprocess .CREATE_NO_WINDOW if sys .platform == "win32" else 0 )
632705 try :
633706 stdout , stderr = process .communicate (timeout = 600 )
0 commit comments