extends Node # Подгружает карту отдельным пакетом при первом входе в портал: в вебе её не качают при старте игры. # Пакет (например backrooms.pck) лежит рядом с index.html. Если сцена уже есть в проекте # (редактор, десктоп-запуск) — качать нечего, переходим сразу. signal enter_failed const PACK_URL_ARG := "--pack-url=" # Для тестов на десктопе: --pack-url=http://127.0.0.1:8000/ var _loaded := {} # имя пакета -> true, чтобы не качать и не монтировать дважды var _busy := false var _layer: CanvasLayer var _label: Label var _bar: ProgressBar func _ready() -> void: process_mode = Node.PROCESS_MODE_ALWAYS func enter(map: String, scene_path: String, pack_name: String, title: String) -> void: if _busy: return if pack_name != "" and not _loaded.has(pack_name) and not ResourceLoader.exists(scene_path): _busy = true var ok := await _download_and_mount(pack_name, title) _busy = false if not ok: enter_failed.emit() return Net.go_to_map(map, scene_path) func _download_and_mount(pack_name: String, title: String) -> bool: _show("Загрузка: %s…" % title) get_tree().paused = true # Пока качаем, мир стоит (камень не должен убить игрока на экране загрузки) var http := HTTPRequest.new() http.process_mode = Node.PROCESS_MODE_ALWAYS http.download_file = "user://" + pack_name add_child(http) var state := {"done": false, "result": -1, "code": 0} http.request_completed.connect(func(result: int, code: int, _h, _b): state.result = result state.code = code state.done = true) var ok := false # no-cache: браузер сверяет файл с сервером (после обновления пакета не подсунет старый из кэша) if http.request(_pack_url(pack_name), ["Cache-Control: no-cache"]) == OK: while not state.done: _update_progress(http, title) await get_tree().process_frame if state.result == HTTPRequest.RESULT_SUCCESS and state.code == 200: ok = ProjectSettings.load_resource_pack("user://" + pack_name, false) # false: файлы основной игры в приоритете http.queue_free() if ok: _loaded[pack_name] = true _hide() else: _label.text = "Не удалось загрузить карту" _bar.visible = false await get_tree().create_timer(2.5).timeout _hide() get_tree().paused = false return ok func _pack_url(pack_name: String) -> String: for arg in OS.get_cmdline_user_args(): if arg.begins_with(PACK_URL_ARG): return arg.substr(PACK_URL_ARG.length()) + pack_name if OS.has_feature("web"): return str(JavaScriptBridge.eval("new URL('%s', window.location.href).href" % pack_name)) return "http://127.0.0.1:8000/" + pack_name # ---------- Экран загрузки ---------- func _show(text: String) -> void: _layer = CanvasLayer.new() _layer.layer = 100 add_child(_layer) var bg := ColorRect.new() bg.color = Color(0, 0, 0, 0.88) bg.mouse_filter = Control.MOUSE_FILTER_IGNORE _layer.add_child(bg) bg.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) _label = Label.new() _label.text = text _label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER _label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER _label.add_theme_font_size_override("font_size", 40) bg.add_child(_label) _label.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) _bar = ProgressBar.new() _bar.custom_minimum_size = Vector2(420, 14) _bar.show_percentage = false _bar.indeterminate = true var track := StyleBoxFlat.new() track.bg_color = Color(1, 1, 1, 0.15) var fill := StyleBoxFlat.new() fill.bg_color = Color(1.0, 0.85, 0.25) _bar.add_theme_stylebox_override("background", track) _bar.add_theme_stylebox_override("fill", fill) bg.add_child(_bar) _bar.set_anchors_and_offsets_preset(Control.PRESET_CENTER_BOTTOM, Control.PRESET_MODE_KEEP_SIZE) _bar.position.y -= 120 func _update_progress(http: HTTPRequest, title: String) -> void: var got := http.get_downloaded_bytes() var total := http.get_body_size() # -1, если сервер не прислал Content-Length (например, gzip) if total > 0: _bar.indeterminate = false _bar.max_value = total _bar.value = got _label.text = "Загрузка: %s\n%.1f / %.1f МБ" % [title, got / 1048576.0, total / 1048576.0] else: _label.text = "Загрузка: %s\n%.1f МБ" % [title, got / 1048576.0] func _hide() -> void: if _layer: _layer.queue_free() _layer = null