Merge remote-tracking branch 'origin/feature/command-line-scene'
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
class_name CliSceneLauncher
|
||||
extends RefCounted
|
||||
## Pure command-line argument parser for the Sandbox Stage scene loader.
|
||||
## No scene-tree or OS access, so it is directly unit-testable. Parses
|
||||
## OS.get_cmdline_user_args() output into a structured {scene_path, play, error}
|
||||
## result; the caller (SandboxStage) is responsible for the error dialog and
|
||||
## autosave policy.
|
||||
|
||||
static func parse(args: Array) -> Dictionary:
|
||||
var scene_path := ""
|
||||
var play := false
|
||||
var used_scene_flag := false
|
||||
var used_positional := false
|
||||
var error := ""
|
||||
var i := 0
|
||||
while i < args.size():
|
||||
var token := String(args[i])
|
||||
if token == "--play":
|
||||
play = true
|
||||
elif token == "--scene":
|
||||
used_scene_flag = true
|
||||
if i + 1 >= args.size():
|
||||
return {"scene_path": "", "play": play, "error": "--scene requires a path."}
|
||||
var value := String(args[i + 1])
|
||||
if value.is_empty():
|
||||
return {"scene_path": "", "play": play, "error": "--scene requires a path."}
|
||||
scene_path = value
|
||||
i += 1
|
||||
elif token.begins_with("--scene="):
|
||||
used_scene_flag = true
|
||||
var value := token.substr("--scene=".length())
|
||||
if value.is_empty():
|
||||
return {"scene_path": "", "play": play, "error": "--scene requires a path."}
|
||||
scene_path = value
|
||||
elif token.begins_with("--"):
|
||||
return {"scene_path": "", "play": play, "error": "Unknown option: %s" % token}
|
||||
else:
|
||||
if used_positional:
|
||||
return {"scene_path": "", "play": play, "error": "Specify the scene with either --scene or a single positional path."}
|
||||
used_positional = true
|
||||
scene_path = token
|
||||
i += 1
|
||||
if used_scene_flag and used_positional:
|
||||
error = "Specify the scene with either --scene or a single positional path."
|
||||
elif play and scene_path.is_empty():
|
||||
error = "--play requires --scene <path>."
|
||||
return {"scene_path": scene_path, "play": play, "error": error}
|
||||
@@ -0,0 +1 @@
|
||||
uid://dmoyg7nq46v07
|
||||
@@ -198,6 +198,8 @@ var _unsaved_changes_dialog: ConfirmationDialog = null
|
||||
var _rename_scene_dialog: ConfirmationDialog = null
|
||||
var _scene_name_edit: LineEdit = null
|
||||
var _missing_assets_dialog: AcceptDialog = null
|
||||
var _cli_error_dialog: AcceptDialog = null
|
||||
var _cli_startup: bool = false
|
||||
|
||||
var _panning: bool = false
|
||||
var _pan_last: Vector2 = Vector2.ZERO
|
||||
@@ -423,7 +425,7 @@ func _ready() -> void:
|
||||
_apply_cursor()
|
||||
_refresh_status()
|
||||
_update_scene_chrome()
|
||||
_check_autosave_recovery()
|
||||
_handle_cli_startup(OS.get_cmdline_user_args())
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
@@ -608,7 +610,7 @@ func _viewport_to_world(viewport_pos: Vector2) -> Vector2:
|
||||
func set_mode(mode: StageMode) -> void:
|
||||
if mode == current_mode:
|
||||
return
|
||||
if mode == StageMode.PLAY:
|
||||
if mode == StageMode.PLAY and not _cli_startup:
|
||||
if not _save_scene_to_path(AUTOSAVE_PATH, true):
|
||||
push_warning("SandboxStage: autosave before Play failed.")
|
||||
current_mode = mode
|
||||
@@ -1459,6 +1461,44 @@ func _check_autosave_recovery() -> void:
|
||||
get_node(NodePath("UI")).add_child(dialog); dialog.popup_centered()
|
||||
|
||||
|
||||
func _handle_cli_startup(args: Array) -> void:
|
||||
var result: Dictionary = CliSceneLauncher.parse(args)
|
||||
if not String(result.get("error", "")).is_empty():
|
||||
_show_cli_error_dialog(String(result["error"]))
|
||||
_check_autosave_recovery()
|
||||
return
|
||||
var scene_path := String(result.get("scene_path", ""))
|
||||
if scene_path.is_empty():
|
||||
_check_autosave_recovery()
|
||||
return
|
||||
if FileAccess.file_exists(AUTOSAVE_PATH):
|
||||
DirAccess.remove_absolute(AUTOSAVE_PATH)
|
||||
var load: Dictionary = _load_cli_scene(scene_path)
|
||||
if not bool(load.get("ok", false)):
|
||||
_show_cli_error_dialog(String(load.get("error", "Could not load scene.")))
|
||||
return
|
||||
if bool(result.get("play", false)):
|
||||
_cli_startup = true
|
||||
set_mode(StageMode.PLAY)
|
||||
_cli_startup = false
|
||||
|
||||
|
||||
func _load_cli_scene(path: String) -> Dictionary:
|
||||
var read: Dictionary = STAGE_SCENE_IO.read_file(path)
|
||||
if not bool(read.get("ok", false)):
|
||||
return {"ok": false, "error": String(read.get("error", "Could not load scene."))}
|
||||
if not _apply_scene_data(read["data"], path, false):
|
||||
return {"ok": false, "error": "Could not apply scene data."}
|
||||
return {"ok": true, "error": ""}
|
||||
|
||||
|
||||
func _show_cli_error_dialog(message: String) -> void:
|
||||
if _cli_error_dialog == null:
|
||||
return
|
||||
_cli_error_dialog.dialog_text = message
|
||||
_cli_error_dialog.popup_centered()
|
||||
|
||||
|
||||
func _notification(what: int) -> void:
|
||||
if what == NOTIFICATION_WM_CLOSE_REQUEST:
|
||||
_request_pending_file_action(PendingFileAction.QUIT)
|
||||
@@ -1911,6 +1951,11 @@ func _build_ui() -> void:
|
||||
_missing_assets_dialog.title = "Missing Scene Assets"
|
||||
_missing_assets_dialog.min_size = Vector2i(560, 0)
|
||||
ui.add_child(_missing_assets_dialog)
|
||||
_cli_error_dialog = AcceptDialog.new()
|
||||
_cli_error_dialog.name = "CliErrorDialog"
|
||||
_cli_error_dialog.title = "Could Not Load Scene"
|
||||
_cli_error_dialog.min_size = Vector2i(560, 0)
|
||||
ui.add_child(_cli_error_dialog)
|
||||
_unsaved_changes_dialog = ConfirmationDialog.new()
|
||||
_unsaved_changes_dialog.name = "UnsavedChangesDialog"; _unsaved_changes_dialog.title = "Unsaved Changes"; _unsaved_changes_dialog.dialog_text = "Save changes before continuing?"; _unsaved_changes_dialog.ok_button_text = "Save"
|
||||
_unsaved_changes_dialog.add_button("Discard", false, "discard")
|
||||
|
||||
Reference in New Issue
Block a user