diff --git a/addons/egoventure/cache/cache_update_dialog.gd b/addons/egoventure/cache/cache_update_dialog.gd new file mode 100644 index 00000000..f6b09b54 --- /dev/null +++ b/addons/egoventure/cache/cache_update_dialog.gd @@ -0,0 +1,264 @@ +tool +class_name CacheUpdateDialog +extends WindowDialog + + +# List of scenes to be scanned in full run mode +var _scene_list: Array +# List of scenes to be scanned in delta run mode +var _scene_list_delta: Array + +# Cache map which gets generated during scan +var _cache_map = CacheMap.new() + +# Timestamp of last change date of cache_map.tres file +var _cache_map_time_modified: int + + +# Show the cache update dialog popup +func show_popup(): + # Configure buttons + get_close_button().hide() + $VBox/HBoxContainer/RunDelta.set_disabled(false) + $VBox/HBoxContainer/RunFull.set_disabled(false) + $VBox/HBoxContainer/Cancel.set_disabled(false) + $VBox/HBoxContainer/RunDelta.call_deferred("grab_focus") + + # Read scene directory path from configuration + var configuration = preload("res://configuration.tres") + var scene_dir = configuration.cache_scene_path + + # Retrieve CacheMap and last modified timestamp + if ResourceLoader.exists("res://cache_map.tres"): + _cache_map = ResourceLoader.load("res://cache_map.tres") + var file = File.new() + _cache_map_time_modified = file.get_modified_time("res://cache_map.tres") + else: + _cache_map_time_modified = 0 + + # Retrieve list of scenes in scene directory + _scene_list.clear() + _scene_list_delta.clear() + _read_scene_list(scene_dir) + + $VBox/SceneCount.text = "The project contains %s scenes. (%s of them were changed after last cache map update)" \ + % [_scene_list.size(), _scene_list_delta.size()] + $VBox/ProgressBar.value = 0.0 + self.popup_centered() + # Resize popup to ensure that it fits to rendered nodes + self.set_size($VBox.get_rect().size+Vector2(20,20)) + + +# Start updating the cache map +# +# ** Parameters ** +# +# - full_mode: indicates whether scan runs in full mode (=true) +# or delta mode (=false) +func _on_Run_pressed(full_mode: bool): + # Deactivate buttons + $VBox/HBoxContainer/RunDelta.set_disabled(true) + $VBox/HBoxContainer/RunFull.set_disabled(true) + $VBox/HBoxContainer/Cancel.set_disabled(true) + # "Verbose mode" button remains active and can be toggled + + var scene_index = 0 + var scene_list + + if full_mode: + _cache_map.map.clear() + scene_list = _scene_list + else: + scene_list = _scene_list_delta + + print("\nUpdate of Cache Map started") + + yield(get_tree(),"idle_frame") # required for progress bar + + # Iterate through all scenes + for scene_name in scene_list: + var scan_result = [0, [] ] + var linked_scenes = [] + + scene_index += 1 + var scene = ResourceLoader.load(scene_name) + if scene.is_class("PackedScene"): + var scene_node = scene.instance() + if $VBox/HBoxContainer/Verbose.pressed: + print("Scan scene " + scene_name) + # Scan the scene to retrieve estimated size in kB and adjacent scenes + scan_result = _scan_scene(scene_node) + scene_node.free() # free up memory of instantiated scene + if $VBox/HBoxContainer/Verbose.pressed: + print("[size(kB), [scene list]] -> " + String(scan_result)) + _cache_map.map[scene_name] = scan_result + + $VBox/ProgressBar.set_value(float(scene_index) / scene_list.size() * 100) + yield(get_tree(),"idle_frame") + + # Save result in fixed resource root directory + var err = ResourceSaver.save("res://cache_map.tres", _cache_map) + if err: + printerr("Saving res://cache_map.tres failed. Error Code %s" % err) + else: + print("Updated Cache Map successfully saved in res://cache_map.tres") + + self.hide() + # Enable buttons + $VBox/HBoxContainer/RunDelta.set_disabled(false) + $VBox/HBoxContainer/RunFull.set_disabled(false) + $VBox/HBoxContainer/Cancel.set_disabled(false) + + +# Close popup when Cancel button is selected +func _on_Cancel_pressed(): + self.hide() + + +# Recursively get all scene filenames from directory and subdirectories +# +# ** Parameters ** +# +# - path: directory path +func _read_scene_list(path: String): + var dir = Directory.new() + var file = File.new() + + if dir.open(path) == OK: + dir.list_dir_begin(true) + var filename = dir.get_next() + while filename != "": + if !dir.current_is_dir(): + # Check whether file is a scene + if filename.match("*.tscn"): + # add to scene list + _scene_list.append(dir.get_current_dir() + "/" + filename) + # add to delta list if scene was modified after last cache map update + if file.get_modified_time(dir.get_current_dir() + "/" + filename) > _cache_map_time_modified: + _scene_list_delta.append(dir.get_current_dir() + "/" + filename) + # add to delta list if scene's gd script was modified after last cache map update + elif ( + file.file_exists(dir.get_current_dir() + "/" + filename.get_basename() + ".gd") \ + and file.get_modified_time(dir.get_current_dir() + "/" + filename.get_basename() + ".gd") \ + > _cache_map_time_modified + ): + _scene_list_delta.append(dir.get_current_dir() + "/" + filename) + else: + # Recursive call to process subdirectory + _read_scene_list(dir.get_current_dir() + "/" + filename) + filename = dir.get_next() + dir.list_dir_end() + + else: + print("An error occurred when trying to open directory %s" % path) + + +# This will scan the scene and will return a size estimate and the adjacent scenes +# +# ** Parameters ** +# +# - scene_node: root node of the scene +# +# **Returns** Array with 2 parameters: +# Array[0]: size estimate +# Array[1]: array of adjacent scenes +func _scan_scene(scene_node: Node) -> Array: + var size_estimate = 0 + var linked_scenes: Array + var cache_include: String + var cache_exclude: String + + # Regular expression to select all comments in script + # The capturing groups are used to ensure that '#' in quotations are not excluded + # Capturing group 1: all strings surrounded by double quotes + # Capturing group 3: all strings surrounded by single quotes + var regex_comment = RegEx.new() + regex_comment.compile("#.*|(\"(#.|[^\"])*\")|(\'(#.|[^\'])*\')") + + # Regular expression for scene resources + var regex_scene = RegEx.new() + regex_scene.compile("res:\\/\\/[\\w\\/]*.tscn") + + # get all sprites and target scenes listed in nodes + for node in _get_all_children(scene_node): + + if node.get_class() == "Sprite" and node.texture != null: + if ResourceLoader.exists(node.texture.resource_path): + size_estimate += node.texture.get_data().get_data().size() + + if ( node.get_class() == "TextureButton" + and "target_scene" in node # include Hotspot (and derived classes) + and not "loading_image" in node # but exclude MapHotspot + ): + var scene_path = node.target_scene + if ( + scene_path != "" + and ResourceLoader.exists(scene_path) + ): + linked_scenes.append(scene_path) + + # remove comments from source code + var scene_script = scene_node.get_script() + var source_code: String + + if scene_script and scene_script.has_source_code(): + source_code = scene_script.source_code + cache_include = "" + cache_exclude = "" + + var regex_matches = regex_comment.search_all(source_code) + for i in range(regex_matches.size() - 1, -1, -1): + # replace regex match only if group 1 and 3 are empty + if regex_matches[i].strings[1] == "" and regex_matches[i].strings[3] == "": + source_code = source_code.substr(0, regex_matches[i].get_start()) + \ + source_code.substr(regex_matches[i].get_end(), -1) + if regex_matches[i].strings[0].begins_with("#EVcache-include"): + # scene(s) listed in this comment will be included in cache + cache_include += regex_matches[i].strings[0] + elif regex_matches[i].strings[0].begins_with("#EVcache-exclude"): + # scene(s) listed in this comment will be excluded from cache + cache_exclude += regex_matches[i].strings[0] + + # add scenes that need to be included to source code + source_code += cache_include + + # scan remaining source code for scene names + var scene_matches = regex_scene.search_all(source_code) + for scene in scene_matches: + var scene_path = scene.get_string() + if ResourceLoader.exists(scene_path): + if not scene_path in linked_scenes: + linked_scenes.append(scene_path) + else: + print("Warning: %s: scene %s was not found" % [scene_script.resource_path, scene_path]) + + # process cache exclusion + var scene_exclude_matches = regex_scene.search_all(cache_exclude) + for scene in scene_exclude_matches: + var scene_path = scene.get_string() + if scene_path in linked_scenes: + linked_scenes.erase(scene_path) + else: + print("Warning: %s: scene %s that should be excluded from cache was not part of cache map" % [scene_script.resource_path, scene_path]) + + # convert size from Byte to kiloByte + size_estimate = size_estimate / 1024 + + return [size_estimate, linked_scenes] + + +# Recursive function to retrieve all child nodes +# +# ** Parameters ** +# +# - node: starting node +# +# ** Returns ** list of all child nodes +func _get_all_children(node: Node)->Array: + var nodes: Array + for child in node.get_children(): + nodes.append(child) + if child.get_child_count() > 0: + nodes.append_array(_get_all_children(child)) + return nodes + diff --git a/addons/egoventure/cache/cache_update_dialog.tscn b/addons/egoventure/cache/cache_update_dialog.tscn new file mode 100644 index 00000000..27419d9f --- /dev/null +++ b/addons/egoventure/cache/cache_update_dialog.tscn @@ -0,0 +1,92 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/egoventure/cache/cache_update_dialog.gd" type="Script" id=1] + +[node name="CacheUpdateDialog" type="WindowDialog"] +margin_right = 791.0 +margin_bottom = 186.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +popup_exclusive = true +window_title = "EgoVenture - Update Cache Map" +script = ExtResource( 1 ) + +[node name="VBox" type="VBoxContainer" parent="."] +margin_left = 10.0 +margin_top = 10.0 +margin_right = 782.0 +margin_bottom = 172.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="Header" type="RichTextLabel" parent="VBox"] +margin_right = 772.0 +margin_bottom = 15.0 +text = "This will scan all scenes in configured scene folder looking for adjacent scenes that will be added to the cache map." +fit_content_height = true + +[node name="SceneCount" type="RichTextLabel" parent="VBox"] +margin_top = 19.0 +margin_right = 772.0 +margin_bottom = 34.0 +text = "This project contains x scenes." +fit_content_height = true + +[node name="Note" type="RichTextLabel" parent="VBox"] +margin_top = 38.0 +margin_right = 772.0 +margin_bottom = 68.0 +text = "Note: Depending on the size of the project the checks might run for several minutes. The result will be printed to Output / Console." +fit_content_height = true + +[node name="Separator" type="HSeparator" parent="VBox"] +margin_top = 72.0 +margin_right = 772.0 +margin_bottom = 92.0 +custom_constants/separation = 20 + +[node name="ProgressBar" type="ProgressBar" parent="VBox"] +margin_top = 96.0 +margin_right = 772.0 +margin_bottom = 110.0 + +[node name="Separator2" type="HSeparator" parent="VBox"] +margin_top = 114.0 +margin_right = 772.0 +margin_bottom = 134.0 +custom_constants/separation = 20 + +[node name="HBoxContainer" type="HBoxContainer" parent="VBox"] +margin_top = 138.0 +margin_right = 772.0 +margin_bottom = 162.0 +grow_horizontal = 0 +custom_constants/separation = 20 + +[node name="RunDelta" type="Button" parent="VBox/HBoxContainer"] +margin_right = 205.0 +margin_bottom = 24.0 +text = "Update Cache Map (Delta Run)" + +[node name="RunFull" type="Button" parent="VBox/HBoxContainer"] +margin_left = 225.0 +margin_right = 419.0 +margin_bottom = 24.0 +text = "Update Cache Map (Full Run)" + +[node name="Cancel" type="Button" parent="VBox/HBoxContainer"] +margin_left = 439.0 +margin_right = 493.0 +margin_bottom = 24.0 +size_flags_horizontal = 2 +text = "Cancel" + +[node name="Verbose" type="CheckBox" parent="VBox/HBoxContainer"] +margin_left = 651.0 +margin_right = 772.0 +margin_bottom = 24.0 +text = "Verbose mode" + +[connection signal="pressed" from="VBox/HBoxContainer/RunDelta" to="." method="_on_Run_pressed" binds= [ false ]] +[connection signal="pressed" from="VBox/HBoxContainer/RunFull" to="." method="_on_Run_pressed" binds= [ true ]] +[connection signal="pressed" from="VBox/HBoxContainer/Cancel" to="." method="_on_Cancel_pressed"] diff --git a/addons/egoventure/cache/scene_cache.gd b/addons/egoventure/cache/scene_cache.gd index 8775e344..eb09503a 100644 --- a/addons/egoventure/cache/scene_cache.gd +++ b/addons/egoventure/cache/scene_cache.gd @@ -11,9 +11,6 @@ signal queue_complete # A cache of scenes for faster switching var _cache: Dictionary -# The regex to cut the scene index from the filename -var _scene_index_regex: RegEx - # Number of scenes to cache before and after the current scene var _cache_count: int @@ -29,6 +26,40 @@ var _queued_items: Array = [] # Do not remove these scenes from the cache var _permanent_cache: PoolStringArray = [] +# CacheMap is an object containing a Dictionary of scenes and cache parameters +# Dictionary key: scene name +# Dictionary value: array of +# - estimated raw size of textures of scene in kB +# - array of scenes adjacent to the scene +var _cache_map: CacheMap + +# Current cache sizes (in kilobyte) +var _cache_size: int +# Maximum cache size (in kilobyte) +var _cache_max_size: int + +# Inner class for parameters of cached scenes +class CacheMgmt: + # Indicates the age when the scene was inserted or updated in the cache + # minus the distance to the current scene + var age: int + # Size of the textures of the scene (in Megabyte) + var size: float + + # Constructor + func _init(age, size): + self.age = age + self.size = size + +# Cache manager - keeping track of the size of cached scenes and when they have been cached (age) +var _cache_mgr: Dictionary + +# Age keeps track when the scene has been added to the cage +# It increments by 1 with every scene change +var _cache_age: int +# Clear Age is an index upto which age scenes have been cleared from cache +var _cache_clear_age: int + # Initialize the cache # @@ -37,21 +68,33 @@ var _permanent_cache: PoolStringArray = [] # - cache_count: The number of scenes to cache before and after the # current scene # - scene_path: The absolute path where scenes are stored -# - scene_regex: A regex to search for the scene index in the scene filename # has to include a named group called "index" +# - permanent_cache: A list of scenes that is cached permanently +# - cache_max_size: Maximum size of cache (w/o permanent cache) in megabyte func _init( cache_count: int, scene_path: String, - scene_regex: String, - permanent_cache: PoolStringArray + permanent_cache: PoolStringArray, + cache_max_size: int ): _cache_count = cache_count _scene_path = scene_path _permanent_cache = permanent_cache - _scene_index_regex = RegEx.new() - _scene_index_regex.compile(scene_regex) _resource_queue = ResourceQueue.new() _resource_queue.start() + + _cache_max_size = cache_max_size * 1024 # MB -> kB + _cache_size = 0 + + if ResourceLoader.exists("res://cache_map.tres"): + _cache_map = ResourceLoader.load("res://cache_map.tres") + else: + _cache_map = CacheMap.new() + + # initialize cache manager + _cache_age = _cache_count # starting age is set to configured cache count + _cache_clear_age = 0 + _cache_mgr.clear() # Update the current progress on the waiting screen and emit the queue_complete @@ -82,8 +125,13 @@ func update_progress(): # - path: The path to the scene func get_scene(path: String) -> PackedScene: if not path in _cache.keys(): - var scene = _resource_queue.get_resource(path) - _cache[path] = scene + # add to cache if scene is part of cache map + if path in _cache_map.map: + var scene = _resource_queue.get_resource(path) + _cache[path] = scene + # otherwise don't cache the scene + else: + return (ResourceLoader.load(path) as PackedScene) return _cache[path] @@ -95,84 +143,131 @@ func get_scene(path: String) -> PackedScene: # - current_scene: The path and filename of the current scene # **Returns** Number of cached scenes func update_cache(current_scene: String) -> int: - if current_scene in _permanent_cache: - # Directly cache permanent scenes - _resource_queue.queue_resource(current_scene) - _queued_items.append(current_scene) - return 1 - - var scene_index = _get_index_from_filename(current_scene) + var scene_list: Dictionary # list of scenes read from cache map + var _cache_updated = false - var first_index = scene_index - _cache_count - if first_index < 1: - first_index = 1 - - var last_index = scene_index + _cache_count - - print_debug( - "Caching scenes from index %d to %d" % [first_index, last_index] - ) - - var base_path - - if EgoVenture.current_location == "": - base_path = _scene_path - else: - base_path = "%s/%s" % [_scene_path, EgoVenture.current_location] - - for cache_item in _cache.keys(): - if not cache_item in _permanent_cache: - if cache_item.get_base_dir() != base_path: - print_debug("Removing scene %s from cache" % cache_item) - _cache.erase(cache_item) - else: - var cache_index = _get_index_from_filename(cache_item) - if cache_index < first_index or cache_index > last_index: - print_debug("Removing scene %s from cache" % cache_item) - _cache.erase(cache_item) + # add current scene to caching + if !current_scene in _permanent_cache: + scene_list[current_scene] = 0 - var scene_directory = Directory.new() - - scene_directory.open(base_path) - - scene_directory.list_dir_begin(true, true) - - var scene: String = scene_directory.get_next() - var path: String - - path = "%s/%s" % [base_path, scene] - - while scene != "": - if not path in _cache.keys() and path.get_extension() == "tscn": - var current_index = _get_index_from_filename(scene) - if current_index >= first_index \ - and current_index <= last_index \ - and not path in _resource_queue.pending.keys(): - print_debug("Queueing load of scene %s" % scene) - _resource_queue.queue_resource(path) - _queued_items.append(path) - - scene = scene_directory.get_next() - path = "%s/%s" % [base_path, scene] + # read surrounding scenes to caching + # note: dictionary 'scene_list' is passed by reference and gets updated + if _cache_count > 0: + _read_cache_map(scene_list, current_scene, 1) - scene_directory.list_dir_end() + for mapped_scene in scene_list.keys(): + if not mapped_scene in _cache_mgr.keys(): # mapped scene is not yet cached + if ( + not mapped_scene in _permanent_cache # mapped scenes in permanent cache don't have to be cached again + and mapped_scene in _cache_map.map # mapped scene listed in cache map + and ResourceLoader.exists(mapped_scene) + ): + var mapped_scene_size = _cache_map.map[mapped_scene][0] + while ( + _cache_size + mapped_scene_size > _cache_max_size and + _cache_age - _cache_count != _cache_clear_age # don't clear cache that was loaded in previous step + ): + _remove_scenes_from_cache(_cache_clear_age) + _cache_clear_age += 1 + + if _cache_size + mapped_scene_size <= _cache_max_size: + _cache_size += mapped_scene_size + _cache_mgr[mapped_scene] = \ + CacheMgmt.new(_cache_age - scene_list[mapped_scene], mapped_scene_size) + if OS.is_debug_build(): + print("Queueing load of mapped scene %s. Age: %s. Cache size: %s." % + [mapped_scene.get_file(), _cache_age - scene_list[mapped_scene], _cache_size]) + _resource_queue.queue_resource(mapped_scene) + _queued_items.append(mapped_scene) + _cache_updated = true + else: + if OS.is_debug_build(): + print("Cache size %s kB reached. Scene %s could not be cached!" % + [_cache_max_size, mapped_scene.get_file()]) + else: + # update age in cache manager + if _cache_mgr[mapped_scene].age != _cache_age - scene_list[mapped_scene]: + _cache_mgr[mapped_scene].age = _cache_age - scene_list[mapped_scene] + _cache_updated = true + + if _cache_updated: + _cache_age += 1 if _queued_items.size() == 0: WaitingScreen.hide() emit_signal("queue_complete") return _queued_items.size() - -# Extract index from filename +# Add a scene to the permanent cache +# Scenes in permanent cache are not handled by cache manager # # ** Parameters ** -# -# filename: The path and filename of the scene -func _get_index_from_filename(filename: String) -> int: - filename = filename.get_file() - var result = _scene_index_regex.search(filename) - if result == null: - return -1 - return int(result.get_string("index")) +# +# - scene: scene that is to be added to permanent cache +func update_permanent_cache(scene: String): + _resource_queue.queue_resource(scene) + _queued_items.append(scene) + + +# Remove scenes from cache +# +# ** Parameters ** +# +# - age_remove: all scenes up to this age get removed +func _remove_scenes_from_cache(age_remove: int): + var scene_removal: Array + + for scene in _cache_mgr: + if(_cache_mgr[scene].age <= age_remove): + scene_removal.append(scene) + + for scene in scene_removal: + _cache_size -= _cache_mgr[scene].size + if OS.is_debug_build(): + print("Removing scene %s from cache. Age: %s. New cache size: %s" % + [scene, _cache_mgr[scene].age, _cache_size]) + _cache.erase(scene) + _cache_mgr.erase(scene) + + +# Print content of cache and cache manager to output +# (for cache verification) +func print_cache_mgr(): + var count = 0 + var sum = 0 + + print("Scenes in cache:") + for scene in _cache: + print("Scene: %s" % scene) + count += 1 + print("Total scenes in cache: %s" % count) + + count = 0 + print("\nScenes in cache manager:") + for scene in _cache_mgr: + print("Age: %s, Size: %s, Scene: %s" % [_cache_mgr[scene].age, _cache_mgr[scene].size, scene]) + count += 1 + sum += _cache_mgr[scene].size + print("Total scenes in cache manager: %s, Total cache size: %s" % [count, sum]) + + +# Retrieve scene list from cache map for scene +# +# ** Parameters ** +# +# - scene_list: list of scenes including index information +# passed by reference, parameter gets updated +# - scene: scene for which cache map is read +# - index: recursion index increasing from 1 to scene_count +func _read_cache_map(scene_list: Dictionary, scene: String, index: int): + var scene_next_list: Array + if _cache_map.map.has(scene): + for surr_scene in _cache_map.map[scene][1]: + if not scene_list.has(surr_scene): + scene_list[surr_scene] = index + if index < _cache_count: + scene_next_list.append(surr_scene) + for scene_next in scene_next_list: + _read_cache_map(scene_list, scene_next, index + 1) diff --git a/addons/egoventure/egoventure.gd b/addons/egoventure/egoventure.gd index 81ac788a..78fe435a 100644 --- a/addons/egoventure/egoventure.gd +++ b/addons/egoventure/egoventure.gd @@ -16,11 +16,6 @@ signal requested_view_change(to) signal waiting_completed -# A regex to search for the scene index in a scene filename. -# e.g.: home04b.tscn has the index 4, castle12detail1.tscn has the index 12. -const SCENE_REGEX = "^[a-z_-]+(?\\d+)\\D?.*$" - - # The current state of the game var state: BaseState @@ -127,8 +122,8 @@ func configure(p_configuration: GameConfiguration): _scene_cache = SceneCache.new( configuration.cache_scene_count, configuration.cache_scene_path, - SCENE_REGEX, - configuration.cache_permanent + configuration.cache_permanent, + configuration.cache_maximum_size_megabyte ) MenuGrab.set_top(configuration.inventory_size) _scene_cache.connect("queue_complete", self, "_on_queue_complete") @@ -165,7 +160,8 @@ func change_scene(path: String): ["res://addons/egoventure/nodes/four_side_room.tscn", "res://addons/egoventure/nodes/eight_side_room.tscn"]: is_multi_side_room = true - + # update cache when scene is changed + update_cache(path) # Set whether dialog line skipping is enabled in parrot # @@ -513,7 +509,7 @@ func _on_quit_game(): func _warm_up_cache(): for scene in configuration.cache_permanent: print_debug("Queueing load of permanent scene %s" % scene) - update_cache(scene) + _scene_cache.update_permanent_cache(scene) # Sets whether the game is currently accepting input or not diff --git a/addons/egoventure/nodes/eight_side_room.gd b/addons/egoventure/nodes/eight_side_room.gd index a1d1164c..8c7f74ad 100644 --- a/addons/egoventure/nodes/eight_side_room.gd +++ b/addons/egoventure/nodes/eight_side_room.gd @@ -62,7 +62,6 @@ func _init(): # Update the cache and position the navigation tools func _ready(): if not Engine.editor_hint: - EgoVenture.update_cache() $Camera/Left.rect_position.x = 0 $Camera/Left.rect_position.y = EgoVenture.configuration.inventory_size $Camera/Left.rect_size.x = EgoVenture.configuration\ diff --git a/addons/egoventure/nodes/four_side_room.gd b/addons/egoventure/nodes/four_side_room.gd index 6cee31cd..e195ae13 100644 --- a/addons/egoventure/nodes/four_side_room.gd +++ b/addons/egoventure/nodes/four_side_room.gd @@ -63,7 +63,6 @@ func _init(): # Update the cache and position the navigation tools func _ready(): if not Engine.editor_hint: - EgoVenture.update_cache() $Camera/Left.rect_position.x = 0 $Camera/Left.rect_position.y = EgoVenture.configuration.inventory_size $Camera/Left.rect_size.x = EgoVenture.configuration\ diff --git a/addons/egoventure/plugin.gd b/addons/egoventure/plugin.gd index 793c79b6..b40004a6 100644 --- a/addons/egoventure/plugin.gd +++ b/addons/egoventure/plugin.gd @@ -3,6 +3,8 @@ tool extends EditorPlugin +var _cache_update_dialog: CacheUpdateDialog + # Add the required singletons, load the default audio bus # layout func _enter_tree(): @@ -54,6 +56,9 @@ func _enter_tree(): "CheckCursor", "res://addons/egoventure/nodes/check_cursor.tscn" ) + add_tool_menu_item("Update Cache Map", self, "_on_cache_update_menu_clicked") + _cache_update_dialog = preload("res://addons/egoventure/cache/cache_update_dialog.tscn").instance() + get_editor_interface().get_editor_viewport().add_child(_cache_update_dialog) # Remove the previously loaded singletons @@ -66,3 +71,10 @@ func _exit_tree(): remove_autoload_singleton('Inventory') remove_autoload_singleton('DetailView') remove_autoload_singleton('CheckCursor') + remove_tool_menu_item("Update Cache Map") + _cache_update_dialog.queue_free() + + +# Show cache update dialog popup +func _on_cache_update_menu_clicked(_ud): + _cache_update_dialog.show_popup() diff --git a/addons/egoventure/resources/cache_map.gd b/addons/egoventure/resources/cache_map.gd new file mode 100644 index 00000000..9db060a4 --- /dev/null +++ b/addons/egoventure/resources/cache_map.gd @@ -0,0 +1,22 @@ +# CacheMap +tool +class_name CacheMap +extends Resource + + +# map is a Dictionary of scenes and cache parameters +# Dictionary key: scene name +# Dictionary value: array of +# - estimated raw size of textures of scene in kB +# - array of scenes adjacent to the scene +var map: Dictionary + + +# Build the property list +func _get_property_list(): + var properties = [] + properties.append({ + name = "map", + type = TYPE_DICTIONARY + }) + return properties diff --git a/addons/egoventure/resources/game_configuration.gd b/addons/egoventure/resources/game_configuration.gd index 007089e5..d2b571ec 100644 --- a/addons/egoventure/resources/game_configuration.gd +++ b/addons/egoventure/resources/game_configuration.gd @@ -127,7 +127,10 @@ var tools_background_fader_seconds: float = 0.5 var cache_scene_path: String = "res://scenes" # Number of scenes to precache before and after the current scene -var cache_scene_count: int = 3 +var cache_scene_count: int = 2 + +# Size of scene cache in MB +var cache_maximum_size_megabyte: int = 50 # A list of scenes (as path to the scene files) that are always cached var cache_permanent: PoolStringArray = [] @@ -401,6 +404,10 @@ func _get_property_list(): name = "cache_scene_count", type = TYPE_INT }) + properties.append({ + name = "cache_maximum_size_megabyte", + type = TYPE_INT + }) properties.append({ name = "cache_permanent", type = TYPE_STRING_ARRAY, diff --git a/cache_map.tres b/cache_map.tres new file mode 100644 index 00000000..26c8476f --- /dev/null +++ b/cache_map.tres @@ -0,0 +1,96 @@ +[gd_resource type="Resource" load_steps=2 format=2] + +[ext_resource path="res://addons/egoventure/resources/cache_map.gd" type="Script" id=1] + +[resource] +script = ExtResource( 1 ) +map = { +"res://scenes/brother/bro1.tscn": [ 10800, [ "res://scenes/misc/map.tscn" ] ], +"res://scenes/intro.tscn": [ 0, [ "res://scenes/misc/map_info.tscn" ] ], +"res://scenes/logo.tscn": [ 10800, [ "res://scenes/menu.tscn" ] ], +"res://scenes/man/man01.tscn": [ 54000, [ "res://scenes/man/man02.tscn", "res://scenes/misc/map.tscn" ] ], +"res://scenes/man/man02.tscn": [ 43200, [ "res://scenes/man/man01.tscn", "res://scenes/man/man03.tscn" ] ], +"res://scenes/man/man03.tscn": [ 43200, [ "res://scenes/man/man03b_cl.tscn", "res://scenes/man/man02.tscn", "res://scenes/man/man03b_inf.tscn", "res://scenes/man/man03b_inf.tscn", "res://scenes/man/man04.tscn", "res://scenes/man/man04a_inf.tscn" ] ], +"res://scenes/man/man03b_cl.tscn": [ 10800, [ "res://scenes/man/man03.tscn" ] ], +"res://scenes/man/man03b_inf.tscn": [ 10800, [ "res://scenes/man/man03b_cl.tscn", "res://scenes/man/man03.tscn" ] ], +"res://scenes/man/man04.tscn": [ 43200, [ "res://scenes/man/man05.tscn", "res://scenes/man/man03.tscn", "res://scenes/man/man14.tscn" ] ], +"res://scenes/man/man04a_inf.tscn": [ 10800, [ "res://scenes/man/man04.tscn" ] ], +"res://scenes/man/man05.tscn": [ 54000, [ "res://scenes/man/man04.tscn", "res://scenes/man/man10.tscn", "res://scenes/man/man06.tscn", "res://scenes/man/man11a_inf.tscn", "res://scenes/man/man11.tscn" ] ], +"res://scenes/man/man06.tscn": [ 43200, [ "res://scenes/man/man06b_cl1.tscn", "res://scenes/man/man06b_cl2.tscn", "res://scenes/man/man05.tscn", "res://scenes/man/man07.tscn", "res://scenes/man/man06c_cl.tscn" ] ], +"res://scenes/man/man06b_cl1.tscn": [ 10800, [ "res://scenes/man/man06.tscn", "res://scenes/man/man06b_cl1_op.tscn" ] ], +"res://scenes/man/man06b_cl1_op.tscn": [ 10800, [ "res://scenes/man/man06b_cl1.tscn" ] ], +"res://scenes/man/man06b_cl2.tscn": [ 10800, [ "res://scenes/man/man06.tscn", "res://scenes/man/man06b_cl2_op.tscn" ] ], +"res://scenes/man/man06b_cl2_op.tscn": [ 10800, [ "res://scenes/man/man06b_cl2.tscn" ] ], +"res://scenes/man/man06c_cl.tscn": [ 10800, [ "res://scenes/man/man06.tscn", "res://scenes/man/man06c_op.tscn" ] ], +"res://scenes/man/man06c_lift1.tscn": [ 10800, [ "res://scenes/man/man06c_lift2.tscn" ] ], +"res://scenes/man/man06c_lift2.tscn": [ 10800, [ "res://scenes/man/man06c_op.tscn" ] ], +"res://scenes/man/man06c_op.tscn": [ 10800, [ "res://scenes/man/man06.tscn", "res://scenes/man/man06c_lift1.tscn" ] ], +"res://scenes/man/man07.tscn": [ 43200, [ "res://scenes/man/man09.tscn", "res://scenes/man/man06.tscn", "res://scenes/man/man08.tscn", "res://scenes/man/man07b_lift1.tscn", "res://scenes/man/man07b_op.tscn" ] ], +"res://scenes/man/man07b_lift1.tscn": [ 10800, [ "res://scenes/man/man07b_lift2.tscn", "res://scenes/man/man07.tscn" ] ], +"res://scenes/man/man07b_lift2.tscn": [ 10800, [ "res://scenes/man/man07b_lift1.tscn", "res://scenes/man/man07.tscn" ] ], +"res://scenes/man/man07b_op.tscn": [ 10800, [ "res://scenes/man/man07.tscn" ] ], +"res://scenes/man/man08.tscn": [ 43200, [ "res://scenes/man/man07.tscn", "res://scenes/man/man23.tscn", "res://scenes/man/man08a_op.tscn" ] ], +"res://scenes/man/man08a_op.tscn": [ 10800, [ "res://scenes/man/man08.tscn" ] ], +"res://scenes/man/man09.tscn": [ 43200, [ "res://scenes/man/man09a_cl.tscn", "res://scenes/man/man07.tscn", "res://scenes/man/man10.tscn", "res://scenes/man/man09c_cl1.tscn", "res://scenes/man/man09c_cl2.tscn" ] ], +"res://scenes/man/man09a_cl.tscn": [ 10800, [ "res://scenes/man/man09.tscn" ] ], +"res://scenes/man/man09c_cl1.tscn": [ 10800, [ "res://scenes/man/man09.tscn", "res://scenes/man/man09c_lift1.tscn" ] ], +"res://scenes/man/man09c_cl2.tscn": [ 10800, [ "res://scenes/man/man09.tscn", "res://scenes/man/man09c_cl2_lift1.tscn" ] ], +"res://scenes/man/man09c_cl2_lift1.tscn": [ 10800, [ "res://scenes/man/man09c_cl2_lift2.tscn" ] ], +"res://scenes/man/man09c_cl2_lift2.tscn": [ 10800, [ "res://scenes/man/man09c_cl2_lift3.tscn" ] ], +"res://scenes/man/man09c_cl2_lift3.tscn": [ 10800, [ "res://scenes/man/man09c_cl2_lift4.tscn", "res://scenes/man/man09c_cl2_lift4_inf.tscn" ] ], +"res://scenes/man/man09c_cl2_lift4.tscn": [ 10800, [ "res://scenes/man/man09c_cl2_lift5.tscn" ] ], +"res://scenes/man/man09c_cl2_lift4_inf.tscn": [ 10800, [ "res://scenes/man/man09c_cl2_lift4.tscn" ] ], +"res://scenes/man/man09c_cl2_lift5.tscn": [ 10800, [ "res://scenes/man/man09c_cl2.tscn" ] ], +"res://scenes/man/man09c_lift1.tscn": [ 11233, [ "res://scenes/man/man09c_lift1_op.tscn", "res://scenes/man/man09c_cl1.tscn" ] ], +"res://scenes/man/man09c_lift1_op.tscn": [ 10800, [ "res://scenes/man/man09c_lift1.tscn" ] ], +"res://scenes/man/man10.tscn": [ 43200, [ "res://scenes/man/man09.tscn", "res://scenes/man/man05.tscn" ] ], +"res://scenes/man/man11.tscn": [ 54000, [ "res://scenes/man/man11c_cl.tscn", "res://scenes/man/man13.tscn", "res://scenes/man/man12.tscn", "res://scenes/man/man11b_op.tscn", "res://scenes/man/man11d_op.tscn", "res://scenes/man/man05.tscn" ] ], +"res://scenes/man/man11a_inf.tscn": [ 10800, [ "res://scenes/man/man12.tscn" ] ], +"res://scenes/man/man11b_op.tscn": [ 10800, [ "res://scenes/man/man11b_op_op.tscn", "res://scenes/man/man11.tscn" ] ], +"res://scenes/man/man11b_op_op.tscn": [ 10800, [ "res://scenes/man/man11b_op.tscn" ] ], +"res://scenes/man/man11c_cl.tscn": [ 10800, [ "res://scenes/man/man11.tscn", "res://scenes/man/man13.tscn" ] ], +"res://scenes/man/man11d_op.tscn": [ 10800, [ "res://scenes/man/man11.tscn" ] ], +"res://scenes/man/man12.tscn": [ 43200, [ "res://scenes/man/man11.tscn", "res://scenes/man/man12b_cl.tscn", "res://scenes/man/man12f_cl.tscn", "res://scenes/man/man12d_cl.tscn" ] ], +"res://scenes/man/man12b_cl.tscn": [ 10800, [ "res://scenes/man/man12.tscn", "res://scenes/man/man12b_op.tscn" ] ], +"res://scenes/man/man12b_op.tscn": [ 10800, [ "res://scenes/man/man12b_cl.tscn" ] ], +"res://scenes/man/man12d_cl.tscn": [ 10800, [ "res://scenes/man/man12.tscn", "res://scenes/man/man12d_lift.tscn" ] ], +"res://scenes/man/man12d_lift.tscn": [ 10800, [ "res://scenes/man/man12.tscn" ] ], +"res://scenes/man/man12f_cl.tscn": [ 12458, [ "res://scenes/man/man12.tscn", "res://scenes/man/man12f_op.tscn" ] ], +"res://scenes/man/man12f_lift0.tscn": [ 10800, [ "res://scenes/man/man12f_op.tscn", "res://scenes/man/man12f_lift1.tscn" ] ], +"res://scenes/man/man12f_lift1.tscn": [ 10800, [ "res://scenes/man/man12f_lift3.tscn", "res://scenes/man/man12f_lift2.tscn", "res://scenes/man/man12f_lift0.tscn" ] ], +"res://scenes/man/man12f_lift2.tscn": [ 10800, [ "res://scenes/man/man12f_lift1.tscn" ] ], +"res://scenes/man/man12f_lift3.tscn": [ 10886, [ "res://scenes/man/man12f_lift1.tscn" ] ], +"res://scenes/man/man12f_lift4.tscn": [ 0, [ ] ], +"res://scenes/man/man12f_op.tscn": [ 10800, [ "res://scenes/man/man12f_cl.tscn", "res://scenes/man/man12f_lift0.tscn" ] ], +"res://scenes/man/man13.tscn": [ 43200, [ "res://scenes/man/man13d_op.tscn", "res://scenes/man/man11c_cl.tscn", "res://scenes/man/man11.tscn", "res://scenes/man/man13b_cl.tscn" ] ], +"res://scenes/man/man13b_cl.tscn": [ 10800, [ "res://scenes/man/man13.tscn", "res://scenes/man/man13b_op.tscn" ] ], +"res://scenes/man/man13b_op.tscn": [ 10800, [ "res://scenes/man/man13b_cl.tscn" ] ], +"res://scenes/man/man13d_lift1.tscn": [ 10927, [ "res://scenes/man/man13d_op.tscn", "res://scenes/man/man13d_lift2.tscn" ] ], +"res://scenes/man/man13d_lift2.tscn": [ 10800, [ "res://scenes/man/man13d_lift3.tscn" ] ], +"res://scenes/man/man13d_lift3.tscn": [ 10800, [ "res://scenes/man/man13d_lift4.tscn" ] ], +"res://scenes/man/man13d_lift4.tscn": [ 10800, [ "res://scenes/misc/license.tscn" ] ], +"res://scenes/man/man13d_op.tscn": [ 10800, [ "res://scenes/man/man13.tscn", "res://scenes/man/man13d_lift1.tscn" ] ], +"res://scenes/man/man14.tscn": [ 43200, [ "res://scenes/man/man15.tscn", "res://scenes/man/man04.tscn" ] ], +"res://scenes/man/man15.tscn": [ 43200, [ "res://scenes/man/man16.tscn", "res://scenes/man/man14.tscn" ] ], +"res://scenes/man/man16.tscn": [ 43200, [ "res://scenes/man/man15.tscn", "res://scenes/man/man17.tscn" ] ], +"res://scenes/man/man17.tscn": [ 43200, [ "res://scenes/man/man18.tscn", "res://scenes/man/man16.tscn", "res://scenes/man/man17b_cl.tscn" ] ], +"res://scenes/man/man17b_cl.tscn": [ 10800, [ "res://scenes/man/man17.tscn" ] ], +"res://scenes/man/man18.tscn": [ 43200, [ "res://scenes/man/man19.tscn", "res://scenes/man/man17.tscn" ] ], +"res://scenes/man/man19.tscn": [ 43200, [ "res://scenes/man/man20.tscn", "res://scenes/man/man18.tscn" ] ], +"res://scenes/man/man20.tscn": [ 43200, [ "res://scenes/man/man21.tscn", "res://scenes/man/man19.tscn", "res://scenes/man/man22.tscn" ] ], +"res://scenes/man/man21.tscn": [ 43200, [ "res://scenes/man/man20.tscn" ] ], +"res://scenes/man/man22.tscn": [ 43200, [ "res://scenes/man/man20.tscn", "res://scenes/man/man22a_cl.tscn" ] ], +"res://scenes/man/man22a_cl.tscn": [ 10800, [ "res://scenes/man/man22a_lift.tscn", "res://scenes/man/man22.tscn", "res://scenes/man/man22a_xcl.tscn" ] ], +"res://scenes/man/man22a_inf.tscn": [ 10800, [ "res://scenes/man/man22a_cl.tscn" ] ], +"res://scenes/man/man22a_lift.tscn": [ 10800, [ "res://scenes/man/man22a_cl.tscn", "res://scenes/man/man22a_inf.tscn" ] ], +"res://scenes/man/man22a_xcl.tscn": [ 10800, [ "res://scenes/man/man22a_cl.tscn" ] ], +"res://scenes/man/man23.tscn": [ 43200, [ "res://scenes/man/man24.tscn", "res://scenes/man/man08.tscn", "res://scenes/man/man23d_op1.tscn", "res://scenes/man/man23d_op2.tscn" ] ], +"res://scenes/man/man23d_op1.tscn": [ 10800, [ "res://scenes/man/man23.tscn" ] ], +"res://scenes/man/man23d_op2.tscn": [ 10800, [ "res://scenes/man/man23.tscn" ] ], +"res://scenes/man/man24.tscn": [ 43200, [ "res://scenes/man/man23.tscn" ] ], +"res://scenes/menu.tscn": [ 0, [ ] ], +"res://scenes/misc/license.tscn": [ 10800, [ ] ], +"res://scenes/misc/map.tscn": [ 10800, [ ] ], +"res://scenes/misc/map_info.tscn": [ 10800, [ "res://scenes/misc/map.tscn" ] ], +"res://scenes/misc/testscene.tscn": [ 0, [ ] ] +} diff --git a/configuration.tres b/configuration.tres index a11d0ad8..5d5b5a63 100644 --- a/configuration.tres +++ b/configuration.tres @@ -179,6 +179,7 @@ tools_music_fader_seconds = 0.5 tools_background_fader_seconds = 0.5 cache_scene_path = "res://scenes" cache_scene_count = 2 -cache_permanent = PoolStringArray( "res://scenes/misc/map.tscn", "res://scenes/man/man14.tscn", "res://scenes/man/man10.tscn" ) +cache_maximum_size_megabyte = 512 +cache_permanent = PoolStringArray( "res://scenes/misc/map.tscn", "res://scenes/menu.tscn" ) cache_minimum_wait_seconds = 4 cache_minimum_wait_skippable = false diff --git a/project.godot b/project.godot index 343afb1c..d45e1556 100644 --- a/project.godot +++ b/project.godot @@ -15,6 +15,16 @@ _global_script_classes=[ { "path": "res://addons/egoventure/resources/base_state.gd" }, { "base": "Resource", +"class": "CacheMap", +"language": "GDScript", +"path": "res://addons/egoventure/resources/cache_map.gd" +}, { +"base": "WindowDialog", +"class": "CacheUpdateDialog", +"language": "GDScript", +"path": "res://addons/egoventure/cache/cache_update_dialog.gd" +}, { +"base": "Resource", "class": "CharacterResource", "language": "GDScript", "path": "res://addons/parrot/resources/character_resource.gd" @@ -136,6 +146,8 @@ _global_script_classes=[ { } ] _global_script_class_icons={ "BaseState": "", +"CacheMap": "", +"CacheUpdateDialog": "", "CharacterResource": "res://addons/parrot/images/character.svg", "Cursor": "", "DialogHotspot": "res://addons/egoventure/images/dialog_hotspot.svg",