diff --git a/dependency.lic b/dependency.lic index 6001ed98cc..065feb097b 100644 --- a/dependency.lic +++ b/dependency.lic @@ -1,10 +1,20 @@ =begin Documentation: https://elanthipedia.play.net/Lich_script_development#dependency + + As of 4.0.0, all gated class definitions (ArgParser, SetupFiles, + ScriptManager) and boot-sequence orchestration have been removed. + Core lich 5.17.2+ provides these natively via sentinels: + CORE_ARGPARSER, CORE_SETUPFILES, CORE_GET_SETTINGS, + CORE_SCRIPT_LOADER, CORE_MAP_OVERRIDES, CORE_DR_STARTUP + + What remains are runtime helper functions (bankbot, slackbot, + reportbot, hometown, verify_script) that dr-scripts still call + and have not yet been upstreamed into core lich. =end -$DEPENDENCY_VERSION = '3.1.0' +$DEPENDENCY_VERSION = '4.0.0' $DR_SCRIPTS_DISCORD_LINK = 'https://discord.gg/f8ne99pVva' -$MIN_LICH_VERSION = '5.17.0' +$MIN_LICH_VERSION = '5.17.2' unless Gem::Version.new(LICH_VERSION) >= Gem::Version.new($MIN_LICH_VERSION) 10.times do @@ -27,1008 +37,7 @@ end echo("Dependency version #{$DEPENDENCY_VERSION}") -debug = UserVars.debug_dependency - -# --- CORE_ARGPARSER gate --- -if Lich::Common.const_defined?(:CORE_ARGPARSER, false) - # Core provides ArgParser via Lich::Common; include Lich::Common (lich.rbw) makes it top-level -else - class ArgParser - def parse_args(data, flex_args = false) - raw_args = variable.first - baselist = variable.drop(1).dup || Array.new - - unless baselist.size == 1 && baselist.grep(/^help$|^\?$|^h$/).any? - - result = data.map { |definition| check_match(definition, baselist.dup, flex_args) }.compact - - return result.first if result.length == 1 - - if result.empty? - echo "***INVALID ARGUMENTS DON'T MATCH ANY PATTERN***" - respond "Provided Arguments: '#{raw_args}'" - elsif result.length > 1 - echo '***INVALID ARGUMENTS MATCH MULTIPLE PATTERNS***' - respond "Provided Arguments: '#{raw_args}'" - end - - end - - display_args(data) - exit - end - - def display_args(data) - data.each do |def_set| - def_set - .select { |x| x[:name].to_s == "script_summary" } - .each { |x| respond " SCRIPT SUMMARY: #{x[:description]} " } - respond '' - respond " SCRIPT CALL FORMAT AND ARG DESCRIPTIONS (arguments in brackets are optional):" - respond " ;#{Script.current.name} " + def_set.map { |x| format_item(x) unless x[:name].to_s == "script_summary" }.join(' ') - def_set - .reject { |x| x[:name].to_s == "script_summary" } - .each { |x| respond " #{(x[:display] || x[:name]).ljust(12)} #{x[:description]} #{x[:options] ? '[' + x[:options].join(', ') + ']' : ''}" } - end - - yaml_data = get_data('help').to_h - yaml_settings = yaml_data.select { |_field, info| info["referenced_by"].include?(Script.current.name) } - - unless yaml_settings.empty? - respond '' - respond " YAML SETTINGS USED:" - yaml_settings.each do |field, info| - setting_line += " #{field}: #{info["description"]} #{info["specific_descriptions"][Script.current.name]}" - setting_line += " [Ex: #{info["example"]}]" unless info["example"].to_s.nil? || info["example"].to_s.empty? - respond setting_line - end - respond "" - end - end - - private - - def matches_def(definition, item) - echo "#{definition}:#{item}" if UserVars.parse_args_debug - return true if definition[:regex] && definition[:regex] =~ item - return true if definition[:options] && definition[:options].find { |option| item =~ /^#{option}#{'$' if definition[:option_exact]}/i } - - false - end - - def check_match(defs, vars, flex) - args = OpenStruct.new - - defs.reject { |x| x[:optional] }.each do |definition| - return nil unless matches_def(definition, vars.first) - - args[definition[:name]] = vars.first.downcase - vars.shift - end - - defs.select { |x| x[:optional] }.each do |definition| - if (match = vars.find { |x| matches_def(definition, x) }) - args[definition[:name]] = match.downcase - vars.delete(match) - end - end - - if flex - args.flex = vars - - profiles = Dir[File.join(SCRIPT_DIR, 'profiles/*.*')] - profiles.each do |profile| - profile = profile[/.*#{Regexp.escape(checkname)}-(\w*).yaml/, 1] - args.to_h.values.each do |arg| - if arg == profile - echo "WARNING: yaml profile '#{checkname}-#{arg}.yaml' matches script argument '#{arg}'." - echo "Favoring the script argument. Rename the file if you intend to call it as a flexed settings file." - end - end - end - else - return nil unless vars.empty? - end - - args - end - - def format_item(definition) - item = definition[:display] || definition[:name] - if definition[:optional] - item = "[#{item}]" - elsif definition[:variable] || definition[:options] - item = "<#{item}>" - end - item - end - end -end # CORE_ARGPARSER gate - -def parse_args(defn, flex_args = false) - ArgParser.new.parse_args(defn, flex_args) -end - -def display_args(defn) - ArgParser.new.display_args(defn) -end - -# --- CORE_SETUPFILES gate --- -if Lich::Common.const_defined?(:CORE_SETUPFILES, false) - # Core provides SetupFiles via Lich::Common; include Lich::Common (lich.rbw) makes it top-level -else - class SetupFiles - include MonitorMixin - - class FileInfo - attr_reader :path, :name, :mtime - - def initialize(path:, name:, data:, mtime:) - @path = path - @name = name - @data = data - @mtime = mtime - end - - def data - Marshal.load(Marshal.dump(@data)) - end - - def peek(property) - Marshal.load(Marshal.dump(@data[property.to_sym])) - end - - def to_s - File.join(@path, @name) - end - - def inspect - "#" - end - end - - def initialize(debug = false) - super() - @files_cache = {} - @debug = debug - end - - def safe_load_yaml(filepath) - OpenStruct.new(YAML.unsafe_load_file(filepath)).to_h - rescue => e - echo('*** ERROR PARSING YAML FILE ***') - echo(e.message) - return {} - end - - def get_settings(character_suffixes = []) - character_suffixes = ['setup', character_suffixes].flatten.compact.uniq - character_filenames = character_suffixes_to_filenames(character_suffixes) - reload_profiles(character_filenames) - initial_include_suffixes = character_filenames.reduce([]) do |result, filename| - result + (cache_get_by_filename(filename).peek('include') || []) - end - initial_include_filenames = initial_include_suffixes.map { |suffix| to_include_filename(suffix) } - include_filenames = resolve_includes_recursively(initial_include_filenames) - echo "#{self.class}::#{__callee__} resolved include_filenames=#{include_filenames}" if @debug - all_files = ['base.yaml', 'base-empty.yaml', include_filenames, character_filenames].flatten - - union_keys = all_files.reduce([]) do |keys, filename| - file_keys = cache_get_by_filename(filename)&.peek('union_keys') || [] - (keys + file_keys).uniq - end - - settings = all_files.reduce({}) do |result, filename| - result.merge(cache_get_by_filename(filename).data || {}) do |key, old_val, new_val| - if union_keys.include?(key.to_s) && old_val.is_a?(Array) && new_val.is_a?(Array) - (old_val + new_val).uniq - else - new_val - end - end - end - transform_settings(settings) - end - - def get_data(type) - filename = to_base_filename(type) - reload_data([filename]) - transform_data(cache_get_by_filename(filename).data) - end - - def reload - reload_profiles(character_suffixes_to_filenames(['setup'])) - reload_data - end - - private - - SCRIPTS_DATA_PATH = File.join(SCRIPT_DIR, 'data') - - if File.exist?(File.join(DATA_DIR, XMLData.game, "base.yaml")) && File.exist?(File.join(DATA_DIR, XMLData.game, "base-empty.yaml")) && File.exist?(File.join(DATA_DIR, XMLData.game, "#{checkname}-setup.yaml")) - echo("Detected game instance-specific files. Loading settings from #{File.join(DATA_DIR, XMLData.game)}") - SCRIPTS_PROFILES_PATH = File.join(DATA_DIR, XMLData.game) - else - SCRIPTS_PROFILES_PATH = File.join(SCRIPT_DIR, 'profiles') - end - - def reload_profiles(filenames = []) - load_files(get_profiles_glob_patterns(filenames)) - end - - def reload_data(filenames = []) - load_files(get_data_glob_patterns(filenames)) - end - - def get_profiles_glob_patterns(filenames = []) - get_glob_patterns(SCRIPTS_PROFILES_PATH, filenames) - end - - def get_data_glob_patterns(filenames = []) - get_glob_patterns(SCRIPTS_DATA_PATH, filenames) - end - - def get_glob_patterns(basepath = '.', filenames = []) - filenames = ["base*.yaml", "include*.yaml", filenames].flatten.compact.uniq - filenames.map { |filename| File.join(basepath, File.basename(filename)) } - end - - def character_suffixes_to_filenames(character_suffixes) - character_suffixes.map { |suffix| to_character_filename(suffix) } - end - - def to_character_filename(suffix) - "#{checkname}-#{suffix}.yaml" - end - - def to_base_filename(suffix) - "base-#{suffix}.yaml" - end - - def to_include_filename(suffix) - "include-#{suffix}.yaml" - end - - def resolve_includes_recursively(filenames, visited = Set.new, include_order = []) - filenames.each do |filename| - next if visited.include?(filename) - - visited << filename - reload_profiles([filename]) - file_info = cache_get_by_filename(filename) - next unless file_info - - nested_suffixes = file_info.peek('include') || [] - echo "#{self.class}::#{__callee__} #{filename} has nested includes: #{nested_suffixes}" if @debug && !nested_suffixes.empty? - nested_filenames = nested_suffixes.map { |suffix| to_include_filename(suffix) } - - resolve_includes_recursively(nested_filenames, visited, include_order) - - include_order << filename - end - include_order - end - - def load_files(glob_patterns = []) - self.synchronize do - echo "#{self.class}::#{__callee__} glob_patterns=#{glob_patterns}" if @debug - glob_patterns.each do |glob_pattern| - Dir.glob(glob_pattern) - .uniq - .select { |filepath| File.file?(filepath) } - .each do |filepath| - filename = File.basename(filepath) - last_modified_date = File.mtime(filepath) - cached_file = cache_get_by_filename(filename) - echo "#{self.class}::#{__callee__} filepath=#{filepath}, last_modified_date=#{last_modified_date}, cached_file=#{cached_file.inspect}" if @debug - if cached_file.mtime != last_modified_date - cache_put_by_filepath(filepath) - end - end - end - end - end - - def cache_put_by_filepath(filepath) - self.synchronize do - echo "#{self.class}::#{__callee__} filepath=#{filepath}" if @debug - @files_cache[File.basename(filepath)] = SetupFiles::FileInfo.new( - path: File.dirname(filepath), - name: File.basename(filepath), - mtime: File.mtime(filepath), - data: safe_load_yaml(filepath) - ) - end - end - - def cache_get_by_filename(filename) - echo "#{self.class}::#{__callee__} filename=#{filename}" if @debug - @files_cache[filename] - end - - def transform_settings(original_settings) - echo "#{self.class}::#{__callee__}" if @debug - - settings = OpenStruct.new(original_settings) - - base_data_items = get_data('items') - base_data_spells = get_data('spells') - base_data_empty = get_data('empty') - - battle_cries_data = base_data_spells.battle_cries - spells_data = base_data_spells.spell_data - empty_data = base_data_empty.empty_values - - empty_data.each { |name, value| settings[name] ||= value } - - get_spell_data_by_name = lambda do |name_to_find| - spell_matches = spells_data.select { |name, _data| name.casecmp?(name_to_find) } - spell_match = spell_matches.first - spell_data = spell_match.last - spell_data['name'] ||= spell_match.first - spell_data - end - - get_spell_data_by_abbrev = lambda do |abbrev_to_find| - spell_matches = spells_data.select { |_name, data| data['abbrev'].casecmp?(abbrev_to_find) } - spell_match = spell_matches.first - spell_data = spell_match.last - spell_data['name'] ||= spell_match.first - spell_data - end - - enrich_spells_with_data_block = lambda do |spell_setting| - return spell_setting unless spell_setting.is_a?(Hash) - - spell_data = get_spell_data_by_name.call(spell_setting['name']) || - get_spell_data_by_abbrev.call(spell_setting['abbrev']) - (spell_data || {}).merge(spell_setting) - end - - enrich_spells_with_name_block = lambda do |spells_map| - spells_map.each do |spell_name, spell_setting| - if spell_setting.is_a?(Hash) - spell_setting['name'] ||= spell_name - end - end - end - - settings.waggle_sets.transform_values! do |spells_map| - if spells_map.is_a?(Hash) - enrich_spells_with_name_block.call(spells_map) - spells_map.transform_values!(&enrich_spells_with_data_block) - else - spells_map - end - end - - [ - settings.buff_spells, - settings.necromancer_healing, - ].each do |spells_map| - enrich_spells_with_name_block.call(spells_map) - spells_map.transform_values!(&enrich_spells_with_data_block) - end - - [ - settings.buff_spells, - settings.combat_spell_training, - settings.cyclic_training_spells, - settings.magic_training, - settings.training_spells, - settings.crafting_training_spells, - settings.necromancer_healing, - ].each do |spells_map| - next unless spells_map.is_a?(Hash) - - spells_map.transform_values!(&enrich_spells_with_data_block) - end - - [ - settings.offensive_spells - ].each do |spells_list| - spells_list.map!(&enrich_spells_with_data_block) - end - - [ - 'crossing_training_sorcery', - ].each do |key| - settings[key] = enrich_spells_with_data_block.call(settings[key]) - end - - settings.offensive_spells.each do |spell_setting| - next unless spell_setting.is_a?(Hash) - - spell_data = spells_data[spell_setting['name']] - is_native_tm = spell_setting['skill'] == 'Targeted Magic' - is_sorcery_tm = spell_setting['skill'] == 'Sorcery' && spell_data['skill'] == 'Targeted Magic' - if is_native_tm || is_sorcery_tm - spell_setting['prep'] ||= 'target' - end - end - - settings.battle_cries.map! do |battle_cry_setting| - next battle_cry_setting unless battle_cry_setting.is_a?(Hash) - - battle_cry_data = battle_cries_data[battle_cry_setting['name']] - (battle_cry_data || {}).merge(battle_cry_setting) - end - - settings.lootables = ( - base_data_items.lootables + - base_data_items.box_nouns + - base_data_items.gem_nouns + - base_data_items.scroll_nouns + - settings.loot_additions - - settings.loot_subtractions - ).uniq - - settings.crossing_training_sorcery_room ||= UserVars.crossing_training_sorcery_room - settings.compost_room ||= UserVars.compost_room - settings.engineering_room ||= UserVars.engineering_room - settings.outfitting_room ||= UserVars.outfitting_room - settings.alchemy_room ||= UserVars.alchemy_room - settings.safe_room ||= UserVars.safe_room - settings.safe_room_id ||= UserVars.safe_room_id - settings.safe_room_empath ||= UserVars.safe_room_empath - settings.safe_room_empaths += (UserVars.safe_room_empaths || []) - settings.slack_username ||= UserVars.slack_username - settings.bankbot_name ||= UserVars.bankbot_name - settings.bankbot_room_id ||= UserVars.bankbot_room_id - settings.prehunt_buffs ||= UserVars.prehunt_buffs - settings.hometown ||= UserVars.hometown - settings.hometown = $HOMETOWN if $HOMETOWN - - disallowed_safe_rooms = [5713] - if disallowed_safe_rooms.include?(settings.safe_room) || disallowed_safe_rooms.include?(settings.safe_room_id) - _respond("#{settings.safe_room} is not a valid safe room setting.") - _respond("Exiting.") - _respond("Please edit your yaml to use a different safe room.") - exit - end - - [ - 'alchemy_room', - 'bankbot_room_id', - 'compost_room', - 'crossing_training_sorcery_room', - 'enchanting_room', - 'engineering_room', - 'feed_cloak_room', - 'forage_override_room', - 'lockpick_room_id', - 'outdoor_room', - 'outfitting_room', - 'prehunt_buffing_room', - 'safe_room', - 'safe_room_id', - 'theurgy_prayer_mat_room' - ].each do |setting_name| - if settings[setting_name].is_a?(Hash) - hometown_value = settings[setting_name][settings.hometown] - settings[setting_name] = hometown_value if hometown_value - end - end - - if !settings.appraisal_training.include?('pouches') && settings.train_appraisal_with_pouches - settings.appraisal_training.append('pouches') - end - if !settings.appraisal_training.include?('gear') && settings.train_appraisal_with_gear - settings.appraisal_training.append('gear') - end - - if !settings.astrology_training.include?('events') && settings.predict_event - settings.astrology_training.append('events') - end - if !settings.astrology_training.include?('ways') && settings.astral_plane_training['train_in_ap'] - settings.astrology_training.append('ways') - end - - return settings - rescue => e - echo "*** ERROR TRANSFORMING SETTINGS IN DEPENDENCY ***" - echo "*** Commonly this due to malformed config in your yaml file ***" - echo e.message - e.backtrace.each { |msg| echo msg } - OpenStruct.new - end - - def transform_data(original_data) - echo "#{self.class}::#{__callee__}" if @debug - - data = OpenStruct.new(original_data) - - return data - rescue => e - echo "*** ERROR MODIFYING DATA IN DEPENDENCY ***" - echo e.message - e.backtrace.each { |msg| echo msg } - OpenStruct.new - end - end -end # CORE_SETUPFILES gate - -# --- CORE_GET_SETTINGS gate --- -unless Lich::Common.const_defined?(:CORE_GET_SETTINGS, false) - $setupfiles = SetupFiles.new(debug) - - def get_settings(character_suffixes = []) - $setupfiles.get_settings(character_suffixes) - end - - def get_data(type) - $setupfiles.get_data(type) - end -end # CORE_GET_SETTINGS gate - -# --- CORE_SCRIPT_LOADER gate --- -unless Lich::Common.const_defined?(:CORE_SCRIPT_LOADER, false) - def custom_require - lambda do |script_names| - script_names = [script_names] unless script_names.is_a?(Array) - - return if script_names.empty? - - script_names.each do |script_name| - next if Script.running?(script_name) - next unless Script.exists?(script_name) - start_script(script_name) - pause 0.05 - snapshot = Time.now - until !Script.running?(script_name) || Time.now - snapshot > 0.25 - pause 0.05 - end - end - end - end -end # CORE_SCRIPT_LOADER gate - -# --- CORE_MAP_OVERRIDES gate --- -unless Lich::Common.const_defined?(:CORE_MAP_OVERRIDES, false) - def make_map_edits - echo("Applying personal map overrides") - base_wayto_overrides = get_settings.base_wayto_overrides - personal_wayto_overrides = get_settings.personal_wayto_overrides - wayto_overrides = base_wayto_overrides.merge(personal_wayto_overrides) - wayto_overrides.each do |_key, values| - start_room_id = values['start_room'].to_i - end_room_id = values['end_room'].to_i - start_room = Map.list[start_room_id] - old_wayto = start_room.wayto["#{end_room_id}"] - old_timeto = start_room.timeto["#{end_room_id}"] - new_wayto = old_wayto - new_timeto = old_timeto - if values['str_proc'] - new_wayto = StringProc.new("#{values['str_proc']}") - end - if values['travel_time'] - new_timeto = Float(values['travel_time'], exception: false) - new_timeto ||= StringProc.new("#{values['travel_time']}") - end - start_room.wayto["#{end_room_id}"] = new_wayto - start_room.timeto["#{end_room_id}"] = new_timeto - end - personal_map_targets = get_settings.personal_map_targets - if personal_map_targets - custom_targets = (GameSettings['custom targets'] || Hash.new) - custom_targets.merge!(personal_map_targets) - GameSettings['custom targets'] = custom_targets - end - end -end # CORE_MAP_OVERRIDES gate - -# --- ScriptManager gate (replaced by ScriptSync in core) --- -unless Lich::Common.const_defined?(:CORE_GET_SETTINGS, false) - class ScriptManager - def initialize(debug) - @debug = debug - @status_url = 'https://api.github.com/repos/elanthia-online/dr-scripts/git/trees/main?recursive=1' - @raw_base_url = 'https://raw.githubusercontent.com/elanthia-online/dr-scripts/main' - - UserVars.autostart_scripts ||= [] - UserVars.autostart_scripts.uniq! - UserVars.autostart_scripts -= ['dependency'] - - Settings['base_versions'] ||= {} - - @request_authorization = load_personal_github_token - @versions = nil - @dependency_updated = false - @downloads_occurred = false - end - - def dependency_updated? - @dependency_updated - end - - def autostarts - (UserVars.autostart_scripts.to_a + SetupFiles.new.get_settings.autostarts.to_a).uniq - end - - def get_versions - @versions ||= build_version_hash - end - - def build_version_hash - Dir[File.join(SCRIPT_DIR, '*.lic')].each_with_object({}) do |path, versions| - file = File.basename(path) - body = File.binread(File.join(SCRIPT_DIR, file)) - sha = Digest::SHA1.hexdigest('blob' + " #{body.size}" + "\0" + body) - versions[file] = sha - end - end - - def get_script(filename, force = false) - filename = filename.dup - download_script(filename, force) - end - - def repo_scripts - $manager.get_status['tree'].map { |element| element['path'] }.select { |file| file =~ /^[^\/]+\.lic$/ && !file.include?('-setup') } - end - - def start_scripts(force = false) - start_time = Time.now - @versions = nil - scripts = repo_scripts - echo("Checking #{scripts.length} scripts for updates...") - updated = 0 - scripts.each do |(name, _)| - updated += 1 if get_script(name, force) - end - if updated > 0 - echo("Updated #{updated} script#{updated == 1 ? '' : 's'}.") - invalidate_status_cache - else - echo("All scripts are up to date.") - end - autostarts.each do |script| - custom_require.call(script) - end - finish_time = Time.now - run_time = finish_time - start_time - echo("Time spent in start_scripts is #{run_time}") if @debug - end - - def download_script(filename, force = false) - return false if filename.nil? || filename.empty? - - echo("downloading:#{filename}") if @debug - info = get_file_status(filename) - return false unless info - return false if get_versions[filename] == info['sha'] && !force - - echo("info:#{info}") if @debug - content = download_raw_file(filename) - return false unless content - - File.open(File.join(SCRIPT_DIR, "#{filename}"), 'wb') { |file| file.print(content) } - @downloads_occurred = true - @dependency_updated = true if filename == 'dependency.lic' - echo("Downloaded #{filename}") - true - end - - def verify_or_make_dir(path) - Dir.mkdir(path) unless Dir.exist?(path) - end - - def setup_data - verify_or_make_dir File.join(SCRIPT_DIR, 'data') - get_status['tree'] - .select { |data| data['path'] =~ %r{^data/} && data['type'] == 'blob' } - .each do |setup_file| - filename = File.basename(setup_file['path']) - echo("downloading #{filename}") if @debug - content = download_raw_file(setup_file['path']) - next unless content - - File.open(File.join(SCRIPT_DIR, "data/#{filename}"), 'wb') { |file| file.print(content) } - end - end - - def check_base_files - start_time = Time.now - verify_or_make_dir File.join(SCRIPT_DIR, 'profiles') - to_update = get_status['tree'] - .select { |data| data['path'] =~ %r{^profiles/base(?:-empty)?\.yaml$} } - .reject { |setup_file| - filename = File.basename(setup_file['path']) - File.exist?(File.join(SCRIPT_DIR, 'profiles', filename)) && setup_file['sha'] == Settings['base_versions'][filename] - } - if to_update.empty? - echo("Base profile files are up to date.") - else - echo("Updating #{to_update.length} base profile file#{to_update.length == 1 ? '' : 's'}...") - to_update.each do |setup_file| - filename = File.basename(setup_file['path']) - content = download_raw_file(setup_file['path']) - next unless content - - File.open(File.join(SCRIPT_DIR, "profiles/#{filename}"), 'wb') { |file| file.print(content) } - Settings['base_versions'][filename] = setup_file['sha'] - echo("Downloaded #{filename}") - end - end - Settings.save - finish_time = Time.now - run_time = finish_time - start_time - echo("Time spent in check_base_files is #{run_time}") if @debug - end - - def check_data_files - start_time = Time.now - verify_or_make_dir File.join(SCRIPT_DIR, 'data') - to_update = get_status['tree'] - .select { |data| data['path'] =~ %r{^data/base.+yaml$} } - .reject { |setup_file| - filename = File.basename(setup_file['path']) - File.exist?(File.join(SCRIPT_DIR, 'data', filename)) && setup_file['sha'] == Settings['base_versions'][filename] - } - if to_update.empty? - echo("Data files are up to date.") - else - echo("Updating #{to_update.length} data file#{to_update.length == 1 ? '' : 's'}...") - to_update.each do |setup_file| - filename = File.basename(setup_file['path']) - content = download_raw_file(setup_file['path']) - next unless content - - File.open(File.join(SCRIPT_DIR, "data/#{filename}"), 'wb') { |file| file.print(content) } - Settings['base_versions'][filename] = setup_file['sha'] - echo("Downloaded #{filename}") - end - end - Settings.save - finish_time = Time.now - run_time = finish_time - start_time - echo("Time spent in check_data_files is #{run_time}") if @debug - end - - def run_script(filename) - filename.sub!(/\.lic$/, '') - echo("refresh:#{filename}") if @debug - stop_script(filename) if Script.running?(filename) - pause 0.1 while Script.running?(filename) - start_script(filename) - end - - def get_file_status(filename) - get_status['tree'].find { |element| element['path'] == filename } - end - - def invalidate_status_cache - echo("Invalidating cached status (files were downloaded)") if @debug - Settings['status'] = nil - Settings['status_time'] = nil - Settings.save - end - - def get_status - if Settings['status'] && Time.now - Settings['status_time'] <= 120 - echo("Returning cached status response") if @debug - return Settings['status'] - end - - Settings['status_time'] = Time.now - Settings['status'] = make_request(@status_url) - Settings.save - return Settings['status'] - end - - def make_request(raw_uri) - echo "make_request:raw_uri = #{raw_uri}" if @debug - uri = URI.parse(raw_uri) - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - - request = Net::HTTP::Get.new(uri.request_uri) - request['Authorization'] = @request_authorization if @request_authorization - - response = http.request(request) - JSON.parse(response.body) - end - - def download_raw_file(path) - raw_url = "#{@raw_base_url}/#{path}" - echo "download_raw_file: #{raw_url}" if @debug - uri = URI.parse(raw_url) - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - - request = Net::HTTP::Get.new(uri.request_uri) - response = http.request(request) - - unless response.code == '200' - echo "Failed to download #{path}: HTTP #{response.code}" - return nil - end - - response.body - end - - def load_personal_github_token - token_path = "#{DATA_DIR}/githubtoken.txt" - return nil unless File.exist?(token_path) - - token = File.read(token_path).strip - if token.empty? - echo "GitHub token file is empty. Using unauthenticated access." - return nil - end - - echo "Using personal GitHub token." - - url = URI("https://api.github.com/rate_limit") - http = Net::HTTP.new(url.host, url.port) - http.use_ssl = true - request = Net::HTTP::Get.new(url) - request['Authorization'] = "Bearer #{token}" - - response = http.request(request) - if response.body =~ /Bad credentials/ - echo "Bad personal GitHub token. Falling back to unauthenticated access." - echo "Please update or remove #{token_path}" - return nil - end - - parsed_response = JSON.parse(response.body) - used = parsed_response["resources"]["core"]["used"].to_i - limit = parsed_response["resources"]["core"]["limit"].to_i - echo "Personal token rate limit usage: #{used}/#{limit}" if @debug - - "Bearer #{token}" - end - - def make_map_edits - echo("Applying personal map overrides") - base_wayto_overrides = get_settings.base_wayto_overrides - personal_wayto_overrides = get_settings.personal_wayto_overrides - wayto_overrides = base_wayto_overrides.merge(personal_wayto_overrides) - wayto_overrides.each do |_key, values| - start_room_id = values['start_room'].to_i - end_room_id = values['end_room'].to_i - start_room = Map.list[start_room_id] - old_wayto = start_room.wayto["#{end_room_id}"] - old_timeto = start_room.timeto["#{end_room_id}"] - new_wayto = old_wayto - new_timeto = old_timeto - if values['str_proc'] - new_wayto = StringProc.new("#{values['str_proc']}") - end - if values['travel_time'] - new_timeto = Float(values['travel_time'], exception: false) - new_timeto ||= StringProc.new("#{values['travel_time']}") - end - start_room.wayto["#{end_room_id}"] = new_wayto - start_room.timeto["#{end_room_id}"] = new_timeto - end - personal_map_targets = get_settings.personal_map_targets - if personal_map_targets - custom_targets = (GameSettings['custom targets'] || Hash.new) - custom_targets.merge!(personal_map_targets) - GameSettings['custom targets'] = custom_targets - end - end - end - - $manager = ScriptManager.new(debug) - - def get_script(script_name) - filename = format_name(script_name) - if $manager.get_script(filename, true) - echo("#{filename} has been updated.") - else - echo("#{filename} could not be found in the repository.") - end - end - - def force_refresh_scripts - echo("Force refreshing all scripts.") - $manager.start_scripts - end - - def list_tracked_scripts - $manager.get_versions.sort - end - - def setup_data - echo("Force checking scripts/data files.") - $manager.setup_data - end -end # ScriptManager gate - -# --- One-shot cleanup of orphaned Settings['autostart'] --- -# The old CORE_AUTOSTART gate merged Settings['autostart'] into -# UserVars.autostart_scripts on every login, making it impossible -# to permanently remove entries. Clear the orphaned data once, -# then this block becomes a no-op on subsequent logins. -if Settings['autostart'] - echo("Clearing orphaned Settings['autostart'] (no longer used).") - Settings['autostart'] = nil - Settings.save -end - -# --- Deprecated autostart helpers --- -# These functions were removed in 3.1.0. Thin stubs remain so that -# scripts or console calls get a clear message instead of NoMethodError. -def autostart(_script_names, _global = true) - echo("DEPRECATED: autostart() has been removed.") - echo("Use the 'autostarts' list in your YAML profile, or ;autostart add