# Hurry Curry! - a game about cooking # Copyright (C) 2025 Hurry Curry! contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, version 3 of the License only. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # extends Node class_name Cli enum Mode { FLAG, OPTION, MULTI_OPTION, POSITIONAL } class Option: var short #: String? var long: String var mode: Mode var help: String func _init(s, l: String, m: Mode, h: String): short = s; long = l; mode = m; help = h static var OPTIONS := [ Option.new("h", "help", Mode.FLAG, "Show help"), Option.new("s", "setting", Mode.MULTI_OPTION, "Per-launch setting override"), Option.new("c", "join-command", Mode.OPTION, "Message to send right after initial joining"), Option.new(null, "render-items", Mode.OPTION, "Render items from text file to images"), Option.new(null, "render-tiles", Mode.OPTION, "Render tiles from text file to images"), Option.new(null, "render-resolution", Mode.OPTION, "Resolution for rendering items or tiles"), Option.new(null, "render-output", Mode.OPTION, "Output directory for rendering items or tiles"), Option.new(null, "connect_address", Mode.POSITIONAL, "Connect to a server directly without menu interaction") ] static var opts = {} #: Dictionary[String, Variant] static func init() -> bool: if not parse(): return false if opts.has("help"): print_help() return false return true static func print_help(): print("OPTIONS:\n") for opt in OPTIONS: var line = "" if opt.mode == Mode.POSITIONAL: line += "<" + opt.long.to_upper() + ">" else: if opt.short: line += "-" + opt.short + ", " line += "--" + opt.long if opt.mode == Mode.OPTION or opt.mode == Mode.MULTI_OPTION: line += " " while line.length() < 25: line += " " line += " " + opt.help print(line) static func parse() -> bool: var args := OS.get_cmdline_user_args() while not args.is_empty(): var arg := args[0] args.remove_at(0) if arg.begins_with("--"): var long = arg.trim_prefix("--") var opt_index = OPTIONS.find_custom(func(x): return x.long == long) if opt_index == -1: push_error("unknown long option \"%s\"" % long) return false if not _parse_opt(args, OPTIONS[opt_index]): return false elif arg.begins_with("-"): for short in arg.trim_prefix("-"): var opt_index = OPTIONS.find_custom(func(x): return x.short == short) if opt_index == -1: push_error("unknown short option \"%s\"" % short) return false if not _parse_opt(args, OPTIONS[opt_index]): return false else: var opt_index = OPTIONS.find_custom(func(x): return x.mode == Mode.POSITIONAL) if opt_index == -1: push_error("no positional arguments") return false var opt = OPTIONS[opt_index] opts[opt.long] = arg print("Parsed options: ", opts) return true static func _parse_opt(args: Array[String], opt: Option) -> bool: match opt.mode: Mode.FLAG: opts[opt.long] = true return true Mode.OPTION: if args.is_empty(): push_error("missing option value") return false opts[opt.long] = trim_apostrophes(args[0]) args.remove_at(0) return true Mode.MULTI_OPTION: if args.is_empty(): push_error("missing option value") return false if not opts.has(opt.long): opts[opt.long] = [] opts[opt.long].push_back(trim_apostrophes(args[0])) args.remove_at(0) return true Mode.POSITIONAL: push_error("positional arg doesnt need flag") return false push_error("unreachable") return false static func trim_apostrophes(s: String) -> String: # Godot doesn't remove apostrophes when adding custom arguments with # customize run instances in the editor. We'll have to remove them manually. if OS.has_feature("editor_runtime"): return s.trim_prefix("\"").trim_prefix("\'").trim_suffix("\"").trim_suffix("\'") return s # Not running in editor