diff --git a/.gitattributes b/.gitattributes index 627661a..3183cf5 100644 --- a/.gitattributes +++ b/.gitattributes @@ -29,13 +29,6 @@ *.webm filter=lfs diff=lfs merge=lfs -text *.zip filter=lfs diff=lfs merge=lfs -text -# Godot -*.tscn filter=lfs diff=lfs merge=lfs -text -*.tres filter=lfs diff=lfs merge=lfs -text -*.scn filter=lfs diff=lfs merge=lfs -text -*.res filter=lfs diff=lfs merge=lfs -text -*.import filter=lfs diff=lfs merge=lfs -text - # Unity *.anim filter=lfs diff=lfs merge=lfs -text *.asset filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 15b07f8..8a41f40 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,12 @@ # Godot-specific ignores .import/ export.cfg -export_presets.cfg + +# Godot 4.1 can let export_presets be commited. This can be changed on an +# example level if any future examples work with Godot <4.1. +# https://github.com/github/gitignore/pull/2827 +# https://github.com/godotengine/godot/pull/76165 +# export_presets.cfg # Imported translations (automatically generated from CSV files) *.translation diff --git a/godot/bomber/Dockerfile b/godot/bomber/Dockerfile deleted file mode 100644 index 1fee748..0000000 --- a/godot/bomber/Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -FROM ubuntu:22.04 AS builder - -# Install Godot & templates -ENV GODOT_VERSION="4.0.2" -RUN apt update -y \ - && apt install -y wget unzip \ - && wget https://downloads.tuxfamily.org/godotengine/${GODOT_VERSION}/Godot_v${GODOT_VERSION}-stable_linux.x86_64.zip \ - && wget https://downloads.tuxfamily.org/godotengine/${GODOT_VERSION}/Godot_v${GODOT_VERSION}-stable_export_templates.tpz - -RUN mkdir -p ~/.cache ~/.config/godot ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable \ - && unzip Godot_v${GODOT_VERSION}-stable_linux.x86_64.zip \ - && mv Godot_v${GODOT_VERSION}-stable_linux.x86_64 /usr/local/bin/godot \ - && unzip Godot_v${GODOT_VERSION}-stable_export_templates.tpz \ - && mv templates/* ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable \ - && rm Godot_v${GODOT_VERSION}-stable_export_templates.tpz Godot_v${GODOT_VERSION}-stable_linux.x86_64.zip - -# Build application -WORKDIR /app -COPY . . -RUN mkdir -p build/linux \ - && godot -v --export-release "Linux/X11" --headless ./build/linux/game.x86_64 - -# === - -FROM ubuntu:22.04 -RUN apt update -y \ - && apt install -y expect-dev \ - && rm -rf /var/lib/apt/lists/* -COPY --from=builder /app/build/linux/ /app - -# Unbuffer output so the logs get flushed -CMD ["sh", "-c", "unbuffer /app/game.x86_64 --verbose --headless -- --server | cat"] - diff --git a/godot/bomber/LICENSE b/godot/bomber/LICENSE index 8108a07..cfa3809 100644 --- a/godot/bomber/LICENSE +++ b/godot/bomber/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Rivet +Copyright (c) 2023 Rivet Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/godot/bomber/README.md b/godot/bomber/README.md index 10cda46..f02da87 100644 --- a/godot/bomber/README.md +++ b/godot/bomber/README.md @@ -1,12 +1,14 @@ -# Bomber +# Multiplayer Bomber -

- -

+A multiplayer implementation of the classical bomberman game. +One of the players should press "host", while the other +should type in his address and press "play". +Language: GDScript -[Visit Tutorial](https://rivet.gg/learn/godot/tutorials/crash-course) +Renderer: GLES 2 +Check out this demo on the asset library: https://godotengine.org/asset-library/asset/139 @@ -31,3 +33,4 @@ [Documentation](https://rivet.gg/learn/godot/tutorials/crash-course#step-4-deploy-to-rivet) +![Screenshot](screenshots/bomber.png) diff --git a/godot/bomber/addons/rivet/api/rivet_api.gd b/godot/bomber/addons/rivet/api/rivet_api.gd new file mode 100644 index 0000000..22488a3 --- /dev/null +++ b/godot/bomber/addons/rivet/api/rivet_api.gd @@ -0,0 +1,132 @@ +class_name RivetApi +const RivetRequest = preload("rivet_request.gd") + +static var CONFIGURATION_CACHE + +static func _get_configuration(): + if CONFIGURATION_CACHE: + return CONFIGURATION_CACHE + + if FileAccess.file_exists(RivetPluginBridge.RIVET_CONFIGURATION_FILE_PATH): + var config_file = ResourceLoader.load(RivetPluginBridge.RIVET_CONFIGURATION_FILE_PATH) + if config_file and 'new' in config_file: + CONFIGURATION_CACHE = config_file.new() + return CONFIGURATION_CACHE + + if FileAccess.file_exists(RivetPluginBridge.RIVET_DEPLOYED_CONFIGURATION_FILE_PATH): + var deployed_config_file = ResourceLoader.load(RivetPluginBridge.RIVET_DEPLOYED_CONFIGURATION_FILE_PATH) + if deployed_config_file and 'new' in deployed_config_file: + CONFIGURATION_CACHE = deployed_config_file.new() + return CONFIGURATION_CACHE + + push_warning("Rivet configuration file not found") + CONFIGURATION_CACHE = null + return CONFIGURATION_CACHE + +static func _get_api_url(): + # Use plugin config if available + var plugin = RivetPluginBridge.get_plugin() + if plugin: + return plugin.api_endpoint + + # Override shipped configuration endpoint + var url_env = OS.get_environment("RIVET_API_ENDPOINT") + if url_env: + return url_env + + # Use configuration shipped with game + var config = _get_configuration() + if config: + return config.api_endpoint + + # Fallback + return "https://api.rivet.gg" + +## Get authorization token used from within only the plugin for cloud-specific +## actions. +static func _get_cloud_token(): + # Use plugin config if available + var plugin = RivetPluginBridge.get_plugin() + if plugin: + return plugin.cloud_token + + OS.crash("Rivet cloud token not found, this should only be called within the plugin") + +## Get authorization token used for making requests from within the game. +## +## The priority of tokens is: +## +## - If in editor, use the plugin token +## - If provided by environment, then use that (allows for testing) +## - Assume config is provided by the game client +static func _get_runtime_token(): + # Use plugin config if available + var plugin = RivetPluginBridge.get_plugin() + if plugin: + return plugin.namespace_token + + # Use configuration shipped with game + var token_env = OS.get_environment("RIVET_TOKEN") + if token_env: + return token_env + + # Use configuration shipped with game + var config = _get_configuration() + if config: + return config.namespace_token + + OS.crash("Rivet token not found, validate a config is shipped with the game in the .rivet folder") + +## Builds the headers for a request, including the authorization token +static func _build_headers(service: String) -> PackedStringArray: + var token = _get_cloud_token() if service == "cloud" else _get_runtime_token() + return [ + "Authorization: Bearer " + token, + ] + +## Builds a URL to Rivet cloud services +static func _build_url(path: String, service: String) -> String: + var path_segments := path.split("/", false) + path_segments.remove_at(0) + return _get_api_url() + "/%s/%s" % [service, "/".join(path_segments)] + +## Gets service name from a path (e.g. /users/123 -> users) +static func _get_service_from_path(path: String) -> String: + var path_segments := path.split("/", false) + return path_segments[0] + +## Creates a POST request to Rivet cloud services +## @experimental +static func POST(owner: Node, path: String, body: Dictionary) -> RivetRequest: + var service := _get_service_from_path(path) + var url := _build_url(path, service) + var body_json := JSON.stringify(body) + + return RivetRequest.new(owner, HTTPClient.METHOD_POST, url, { + "headers": _build_headers(service), + "body": body_json + }) + +## Creates a GET request to Rivet cloud services +## @experimental +static func GET(owner: Node, path: String, body: Dictionary) -> RivetRequest: + var service := _get_service_from_path(path) + var url := _build_url(path, service) + var body_json := JSON.stringify(body) + + return RivetRequest.new(owner, HTTPClient.METHOD_GET, url, { + "headers": _build_headers(service), + "body": body_json + }) + +## Creates a PUT request to Rivet cloud services +## @experimental +static func PUT(owner: Node, path: String, body: Dictionary) -> RivetRequest: + var service := _get_service_from_path(path) + var url := _build_url(path, service) + var body_json := JSON.stringify(body) + + return RivetRequest.new(owner, HTTPClient.METHOD_PUT, url, { + "headers": _build_headers(service), + "body": body_json + }) diff --git a/godot/bomber/addons/rivet/api/rivet_packages.gd b/godot/bomber/addons/rivet/api/rivet_packages.gd new file mode 100644 index 0000000..5f0bcd7 --- /dev/null +++ b/godot/bomber/addons/rivet/api/rivet_packages.gd @@ -0,0 +1,97 @@ +const _RivetResponse = preload("rivet_response.gd") + +## Lobbies +## @experimental +class Lobbies: + ## Finds a lobby based on the given criteria. If a lobby is not found and + ## prevent_auto_create_lobby is true, a new lobby will be created. + ## + ## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/find}[/url] + func find(body: Dictionary = {}): + return await Rivet.POST("matchmaker/lobbies/find", body).wait_completed() + + ## Joins a specific lobby. This request will use the direct player count + ## configured for the lobby group. + ## + ## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/join}[/url] + func join(body: Dictionary = {}) -> _RivetResponse: + return await Rivet.POST("matchmaker/lobbies/join", body).wait_completed() + + ## Marks the current lobby as ready to accept connections. Players will not + ## be able to connect to this lobby until the lobby is flagged as ready. + ## + ## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/ready}[/url] + func ready(body: Dictionary = {}) -> _RivetResponse: + return await Rivet.POST("matchmaker/lobbies/ready", body).wait_completed() + + ## If is_closed is true, the matchmaker will no longer route players to the + ## lobby. Players can still join using the /join endpoint (this can be disabled + ## by the developer by rejecting all new connections after setting the lobby + ## to closed). Does not shutdown the lobby. + ## + ## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/set-closed}[/url] + func setClosed(body: Dictionary = {}) -> _RivetResponse: + return await Rivet.PUT("matchmaker/lobbies/set_closed", body).wait_completed() + + ## Creates a custom lobby. + ## + ## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/create}[/url] + func create(body: Dictionary = {}) -> _RivetResponse: + return await Rivet.POST("matchmaker/lobbies/create", body).wait_completed() + + ## Lists all open lobbies. + ## + ## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/list}[/url] + func list(body: Dictionary = {}) -> _RivetResponse: + return await Rivet.GET("matchmaker/lobbies/list", body).wait_completed() + + ## + ## + ## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/set-state}[/url] + func setState(body: Dictionary = {}) -> _RivetResponse: + return await Rivet.PUT("matchmaker/lobbies/state", body).wait_completed() + + ## + ## + ## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/get-state}[/url] + func getState(lobby_id, body: Dictionary = {}) -> _RivetResponse: + return await Rivet.GET("matchmaker/lobbies/{lobby_id}/state".format({"lobby_id": lobby_id}), body).wait_completed() + +## Players +## @experimental +class Players: + ## Validates the player token is valid and has not already been consumed then + ## marks the player as connected. + ## + ## [url]{https://rivet.gg/docs/matchmaker/api/players/connected}[/url] + func connected(body: Dictionary = {}) -> _RivetResponse: + return await Rivet.POST("matchmaker/players/connected", body).wait_completed() + + ## Marks a player as disconnected. # Ghost Players. + ## + ## [url]{https://rivet.gg/docs/matchmaker/api/players/disconnected}[/url] + func disconnected(body: Dictionary = {}) -> _RivetResponse: + return await Rivet.POST("matchmaker/players/disconnected", body).wait_completed() + + ## Gives matchmaker statistics about the players in game. + ## + ## [url]{https://rivet.gg/docs/matchmaker/api/players/statistics}[/url] + func getStatistics(body: Dictionary = {}) -> _RivetResponse: + return await Rivet.GET("matchmaker/players/statistics", body).wait_completed() + +class Regions: + ## Returns a list of regions available to this namespace. + ## Regions are sorted by most optimal to least optimal. + ## The player's IP address is used to calculate the regions' optimality. + ## + ## [url]{https://rivet.gg/docs/matchmaker/api/regions/list}[/url] + func list(body: Dictionary = {}) -> _RivetResponse: + return await Rivet.GET("matchmaker/regions", body).wait_completed() + +## Matchmaker +## @experimental +## @tutorial: https://rivet.gg/docs/matchmaker +class Matchmaker: + static var lobbies: Lobbies = Lobbies.new() + static var players: Players = Players.new() + static var regions: Regions = Regions.new() diff --git a/godot/bomber/addons/rivet/api/rivet_request.gd b/godot/bomber/addons/rivet/api/rivet_request.gd new file mode 100644 index 0000000..95bb483 --- /dev/null +++ b/godot/bomber/addons/rivet/api/rivet_request.gd @@ -0,0 +1,57 @@ +extends RefCounted +## A wrapper around HTTPRequest that emits a signal when the request is completed. +## This is a workaround for the fact that `HTTPRequest.request()` is blocking. +## To run a request, create a new RivetRequest, connect to the completed signal, +## and call `request().wait_completed()` to wait for the request to complete. + + +const _RivetResponse := preload("rivet_response.gd") +const _RivetRequest := preload("rivet_request.gd") + +var response: _RivetResponse = null +var _opts: Dictionary +var _http_request: HTTPRequest + +var _success_callback: Callable +var _failure_callback: Callable + +signal completed(response: _RivetResponse) +signal succeeded(response: _RivetResponse) +signal failed(response: _RivetResponse) + +func _init(owner: Node, method: HTTPClient.Method, url: String, opts: Variant = null): + self._http_request = HTTPRequest.new() + self._http_request.request_completed.connect(_on_request_completed) + self._opts = { + "method": method, + "url": url, + "body": opts.body, + "headers": opts.headers, + } + owner.add_child(self._http_request) + self._http_request.request(_opts.url, _opts.headers, _opts.method, _opts.body) + +func set_success_callback(callback: Callable) -> _RivetRequest: + self._success_callback = callback + return self + +func set_failure_callback(callback: Callable) -> _RivetRequest: + self._failure_callback = callback + return self + +func _on_request_completed(result, response_code, headers, body): + self.response = _RivetResponse.new(result, response_code, headers, body) + if result == OK: + succeeded.emit(response) + if self._success_callback: + self._success_callback.call(response) + else: + failed.emit(response) + if self._failure_callback: + self._failure_callback.call(response) + completed.emit(response) + +## Waits for the request to complete and returns the response in non-blocking way +func wait_completed() -> _RivetResponse: + await completed + return response diff --git a/godot/bomber/addons/rivet/api/rivet_response.gd b/godot/bomber/addons/rivet/api/rivet_response.gd new file mode 100644 index 0000000..be954f9 --- /dev/null +++ b/godot/bomber/addons/rivet/api/rivet_response.gd @@ -0,0 +1,27 @@ +extends RefCounted +## A response from the server. Contains the result, response code, headers, and body. +## The body is a dictionary of the JSON response. +## +## @experimental + +## The result of the request. 0 is success, 1 is failure. +var result: HTTPClient.Status + +## The response code from the server. +var response_code: HTTPClient.ResponseCode + +## The headers from the server. +var headers: PackedStringArray + +## The body of the response, as a JSON dictionary, could be a null. +var body: Variant + +func _init(result: int, response_code: int, headers: PackedStringArray, response_body: PackedByteArray) -> void: + self.result = result + self.response_code = response_code + self.headers = headers + + var json = JSON.new() + json.parse(response_body.get_string_from_utf8()) + body = json.get_data() + diff --git a/godot/bomber/addons/rivet/devtools/dock/deploy_tab.gd b/godot/bomber/addons/rivet/devtools/dock/deploy_tab.gd new file mode 100644 index 0000000..c2654ac --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/deploy_tab.gd @@ -0,0 +1,33 @@ +@tool extends MarginContainer + +@onready var namespace_selector: OptionButton = %DeployNamespaceSelector +@onready var manage_versions_button: Button = %ManageVersionButton +@onready var build_deploy_button: Button = %BuildDeployButton + +func _ready() -> void: + manage_versions_button.pressed.connect(_on_manage_versions_button_pressed) + build_deploy_button.pressed.connect(_on_build_deploy_button_pressed) + +func _on_manage_versions_button_pressed() -> void: + _all_actions_set_disabled(true) + + var result = await RivetPluginBridge.get_plugin().cli.run_command(["sidekick", "get-version", "--namespace", namespace_selector.current_value.namespace_id]) + if result.exit_code != 0 or !("Ok" in result.output): + RivetPluginBridge.display_cli_error(self, result) + + OS.shell_open(result.output["Ok"]["output"]) + _all_actions_set_disabled(false) + +func _on_build_deploy_button_pressed() -> void: + _all_actions_set_disabled(true) + + var result = await RivetPluginBridge.get_plugin().cli.run_command(["sidekick", "--show-terminal", "deploy", "--namespace", namespace_selector.current_value.name_id]) + if result.exit_code != 0: + RivetPluginBridge.display_cli_error(self, result) + + _all_actions_set_disabled(false) + +func _all_actions_set_disabled(disabled: bool) -> void: + namespace_selector.disabled = disabled + manage_versions_button.disabled = disabled + build_deploy_button.disabled = disabled diff --git a/godot/bomber/addons/rivet/devtools/dock/deploy_tab.tscn b/godot/bomber/addons/rivet/devtools/dock/deploy_tab.tscn new file mode 100644 index 0000000..d4608b5 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/deploy_tab.tscn @@ -0,0 +1,56 @@ +[gd_scene load_steps=4 format=3 uid="uid://soum1c8oyrso"] + +[ext_resource type="Script" path="res://addons/rivet/devtools/dock/deploy_tab.gd" id="1_7k6ip"] +[ext_resource type="PackedScene" uid="uid://bogw8dj8rr202" path="res://addons/rivet/devtools/dock/elements/namespace_menu_button.tscn" id="2_5hxk2"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_dfadg"] + +[node name="Deploy" type="MarginContainer"] +offset_right = 734.0 +offset_bottom = 109.0 +script = ExtResource("1_7k6ip") + +[node name="Deployment Fieds" type="VBoxContainer" parent="."] +layout_mode = 2 + +[node name="Deployment Label" type="RichTextLabel" parent="Deployment Fieds"] +layout_mode = 2 +theme_override_styles/normal = SubResource("StyleBoxEmpty_dfadg") +bbcode_enabled = true +text = "[b]Build & Deploy Server[/b]" +fit_content = true +scroll_active = false +shortcut_keys_enabled = false + +[node name="DeployNamespaceSelector" parent="Deployment Fieds" instance=ExtResource("2_5hxk2")] +layout_mode = 2 +selected = -1 +current_value = { +"create_ts": "2023-11-19T05:46:56.378Z", +"display_name": "Production", +"name_id": "prod", +"namespace_id": "215bd313-b4c7-4932-b9bd-64f8f09b7fe8", +"version": { +"create_ts": "2023-11-19T05:46:56.171Z", +"display_name": "0.0.1", +"version_id": "73881376-21fa-4fb2-93f2-2f76fb3bac19" +}, +"version_id": "73881376-21fa-4fb2-93f2-2f76fb3bac19" +} + +[node name="Actions" type="HBoxContainer" parent="Deployment Fieds"] +layout_mode = 2 + +[node name="BuildDeployButton" type="Button" parent="Deployment Fieds/Actions"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +mouse_default_cursor_shape = 2 +text = "Build & Deploy" + +[node name="ManageVersionButton" type="Button" parent="Deployment Fieds/Actions"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +mouse_default_cursor_shape = 2 +text = "Manage Versions" diff --git a/godot/bomber/addons/rivet/devtools/dock/dock.gd b/godot/bomber/addons/rivet/devtools/dock/dock.gd new file mode 100644 index 0000000..5eac679 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/dock.gd @@ -0,0 +1,28 @@ +@tool extends Control +## Mainpoint of the plugin's UI + +## Enum representing indexes of the children of this node +enum Screen { + Login, + Settings, + Loading, + Installer, +} + +func _ready() -> void: + change_current_screen(Screen.Installer) + + +func reload() -> void: + var instance = load("res://addons/rivet/devtools/dock/dock.tscn").instantiate() + replace_by(instance) + instance.grab_focus() + + +func change_current_screen(scene: Screen): + for idx in get_child_count(): + var child := get_child(idx) + if "visible" in child: + child.visible = idx == scene + if idx == scene and child.has_method("prepare"): + child.prepare() diff --git a/godot/bomber/addons/rivet/devtools/dock/dock.tscn b/godot/bomber/addons/rivet/devtools/dock/dock.tscn new file mode 100644 index 0000000..ba952fd --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/dock.tscn @@ -0,0 +1,34 @@ +[gd_scene load_steps=6 format=3 uid="uid://l8cfdaru7ibw"] + +[ext_resource type="Script" path="res://addons/rivet/devtools/dock/dock.gd" id="1_xspk7"] +[ext_resource type="PackedScene" uid="uid://mag2n5yvyus8" path="res://addons/rivet/devtools/dock/login.tscn" id="2_qo12a"] +[ext_resource type="PackedScene" uid="uid://ceovepvn1782o" path="res://addons/rivet/devtools/dock/settings.tscn" id="3_8srmc"] +[ext_resource type="PackedScene" uid="uid://cpiafwq88eamc" path="res://addons/rivet/devtools/dock/loading.tscn" id="4_mdhqv"] +[ext_resource type="PackedScene" uid="uid://d3l0arylk0h43" path="res://addons/rivet/devtools/dock/installer.tscn" id="5_gdmi1"] + +[node name="Rivet" type="MarginContainer"] +offset_top = 92.0 +offset_right = 1152.0 +offset_bottom = 92.0 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 2 +theme_override_constants/margin_top = 8 +theme_override_constants/margin_right = 2 +theme_override_constants/margin_bottom = 8 +script = ExtResource("1_xspk7") + +[node name="Login" parent="." instance=ExtResource("2_qo12a")] +visible = false +layout_mode = 2 + +[node name="Settings" parent="." instance=ExtResource("3_8srmc")] +visible = false +layout_mode = 2 + +[node name="Loading" parent="." instance=ExtResource("4_mdhqv")] +visible = false +layout_mode = 2 + +[node name="Installer" parent="." instance=ExtResource("5_gdmi1")] +layout_mode = 2 diff --git a/godot/bomber/addons/rivet/devtools/dock/elements/buttons_bar.gd b/godot/bomber/addons/rivet/devtools/dock/elements/buttons_bar.gd new file mode 100644 index 0000000..2a1cecd --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/elements/buttons_bar.gd @@ -0,0 +1,38 @@ +@tool extends HBoxContainer + +signal selected() + +@export var tab_container: TabContainer + +var disabled: bool = false: + set(value): + disabled = value + for i in get_child_count(): + var child = get_child(i) + if child is Button: + child.disabled = disabled + +var current = 0 + +func _ready() -> void: + for i in get_child_count(): + var child = get_child(i) + if child is Button: + child.toggle_mode = true + child.pressed.connect(_select_button.bind(i)) + if i == 0: + child.set_pressed_no_signal(true) + +func _select_button(curr: int) -> void: + current = curr + if tab_container: + tab_container.set_current_tab(curr) + for i in get_child_count(): + var child = get_child(i) + if child is Button: + child.set_pressed_no_signal(curr==i) + selected.emit() + + +func set_current_button(button: int) -> void: + _select_button(button) \ No newline at end of file diff --git a/godot/bomber/addons/rivet/devtools/dock/elements/buttons_bar.tscn b/godot/bomber/addons/rivet/devtools/dock/elements/buttons_bar.tscn new file mode 100644 index 0000000..6dc4e64 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/elements/buttons_bar.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://bdpu38hakasqq"] + +[ext_resource type="Script" path="res://addons/rivet/devtools/dock/elements/buttons_bar.gd" id="1_mg5dk"] + +[node name="ButtonsBar" type="HBoxContainer"] +script = ExtResource("1_mg5dk") diff --git a/godot/bomber/addons/rivet/devtools/dock/elements/links_container.tscn b/godot/bomber/addons/rivet/devtools/dock/elements/links_container.tscn new file mode 100644 index 0000000..11e5e2c --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/elements/links_container.tscn @@ -0,0 +1,20 @@ +[gd_scene format=3 uid="uid://bk1uwgw1hhq2p"] + +[node name="LinksContainer" type="HBoxContainer"] +theme_override_constants/separation = 16 +alignment = 1 + +[node name="HubLink" type="LinkButton" parent="."] +layout_mode = 2 +text = "Hub" +uri = "https://hub.rivet.gg/" + +[node name="DocsLink" type="LinkButton" parent="."] +layout_mode = 2 +text = "Docs" +uri = "https://rivet.gg/docs" + +[node name="DiscordLink" type="LinkButton" parent="."] +layout_mode = 2 +text = "Discord" +uri = "https://rivet.gg/discord" diff --git a/godot/bomber/addons/rivet/devtools/dock/elements/loading_button.gd b/godot/bomber/addons/rivet/devtools/dock/elements/loading_button.gd new file mode 100644 index 0000000..20e94c5 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/elements/loading_button.gd @@ -0,0 +1,32 @@ +@tool extends Button + +@export var loading: bool: set = _set_loading + +var _tween: Tween + +func _set_loading(value) -> void: + loading = value + disabled = value + + if _tween: + _tween.kill() + + if value: + _tween = get_tree().create_tween() + + var icons: Array[Texture2D] = [ + get_theme_icon("Progress1", "EditorIcons"), + get_theme_icon("Progress2", "EditorIcons"), + get_theme_icon("Progress3", "EditorIcons"), + get_theme_icon("Progress4", "EditorIcons"), + get_theme_icon("Progress5", "EditorIcons"), + get_theme_icon("Progress6", "EditorIcons"), + get_theme_icon("Progress7", "EditorIcons"), + get_theme_icon("Progress8", "EditorIcons"), + get_theme_icon("Progress9", "EditorIcons"), + ] + for idx in icons.size(): + _tween.tween_property(self, "icon", icons[idx], 0 if idx == 0 else 1) + _tween.set_loops() + else: + icon = null \ No newline at end of file diff --git a/godot/bomber/addons/rivet/devtools/dock/elements/loading_button.tscn b/godot/bomber/addons/rivet/devtools/dock/elements/loading_button.tscn new file mode 100644 index 0000000..824cb05 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/elements/loading_button.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=2 format=3 uid="uid://cdad7w76me3eu"] + +[ext_resource type="Script" path="res://addons/rivet/devtools/dock/elements/loading_button.gd" id="1_4ofna"] + +[node name="LoadingButton" type="Button"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/h_separation = 8 +script = ExtResource("1_4ofna") diff --git a/godot/bomber/addons/rivet/devtools/dock/elements/logo_container.gd b/godot/bomber/addons/rivet/devtools/dock/elements/logo_container.gd new file mode 100644 index 0000000..d253415 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/elements/logo_container.gd @@ -0,0 +1,9 @@ +@tool extends HBoxContainer + +@onready var logo: TextureRect = %Logo +var logo_dark = preload("../../../images/icon-text-black.svg") +var logo_light = preload("../../../images/icon-text-white.svg") + +func _ready() -> void: + var is_dark = get_theme_color("font_color", "Editor").get_luminance() < 0.5 + logo.texture = logo_dark if is_dark else logo_light diff --git a/godot/bomber/addons/rivet/devtools/dock/elements/logo_container.tscn b/godot/bomber/addons/rivet/devtools/dock/elements/logo_container.tscn new file mode 100644 index 0000000..3147797 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/elements/logo_container.tscn @@ -0,0 +1,19 @@ +[gd_scene load_steps=3 format=3 uid="uid://dldxcm1l8nnnf"] + +[ext_resource type="Script" path="res://addons/rivet/devtools/dock/elements/logo_container.gd" id="1_pgpb4"] +[ext_resource type="Texture2D" uid="uid://10vqh72x3wrr" path="res://addons/rivet/images/icon-text-black.svg" id="2_c8wnq"] + +[node name="LogoContainer" type="HBoxContainer"] +size_flags_horizontal = 3 +alignment = 1 +script = ExtResource("1_pgpb4") + +[node name="Logo" type="TextureRect" parent="."] +unique_name_in_owner = true +custom_minimum_size = Vector2(2.08165e-12, 40) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 4 +texture = ExtResource("2_c8wnq") +expand_mode = 1 +stretch_mode = 5 diff --git a/godot/bomber/addons/rivet/devtools/dock/elements/namespace_menu_button.gd b/godot/bomber/addons/rivet/devtools/dock/elements/namespace_menu_button.gd new file mode 100644 index 0000000..256ac6a --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/elements/namespace_menu_button.gd @@ -0,0 +1,31 @@ +@tool extends OptionButton +## A control that displays a list of namespaces and allows the user to select one. + +@export var current_value: Dictionary + +var namespaces: Array: + get: return RivetPluginBridge.instance.game_namespaces + +func _ready(): + if RivetPluginBridge.is_part_of_edited_scene(self): + return + disabled = true + _update_menu_button(namespaces) + item_selected.connect(_on_item_selected) + RivetPluginBridge.instance.bootstrapped.connect(_on_plugin_bootstrapped) + +func _update_menu_button(value: Array) -> void: + clear() + for i in value.size(): + add_item("%s (v%s)" % [namespaces[i].display_name, namespaces[i].version.display_name], i) + +func _on_item_selected(idx: int): + _select_menu_item(idx) + +func _select_menu_item(idx: int) -> void: + current_value = namespaces[idx] + +func _on_plugin_bootstrapped() -> void: + disabled = false + _update_menu_button(namespaces) + _select_menu_item(0) \ No newline at end of file diff --git a/godot/bomber/addons/rivet/devtools/dock/elements/namespace_menu_button.tscn b/godot/bomber/addons/rivet/devtools/dock/elements/namespace_menu_button.tscn new file mode 100644 index 0000000..a924ae0 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/elements/namespace_menu_button.tscn @@ -0,0 +1,11 @@ +[gd_scene load_steps=2 format=3 uid="uid://bogw8dj8rr202"] + +[ext_resource type="Script" path="res://addons/rivet/devtools/dock/elements/namespace_menu_button.gd" id="1_c2uah"] + +[node name="NamespaceMenuButton" type="OptionButton"] +unique_name_in_owner = true +offset_right = 190.0 +offset_bottom = 31.0 +size_flags_horizontal = 3 +disabled = true +script = ExtResource("1_c2uah") diff --git a/godot/bomber/addons/rivet/devtools/dock/installer.gd b/godot/bomber/addons/rivet/devtools/dock/installer.gd new file mode 100644 index 0000000..7c13b86 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/installer.gd @@ -0,0 +1,37 @@ +@tool extends Control + +@onready var InstallButton: Button = %InstallButton +@onready var InstallDialog: AcceptDialog = %InstallDialog +@onready var InstallLabel: RichTextLabel = %InstallLabel + +func prepare() -> void: + InstallLabel.add_theme_font_override(&"mono_font", get_theme_font(&"output_source_mono", &"EditorFonts")) + InstallLabel.add_theme_font_override(&"bold_font", get_theme_font(&"bold", &"EditorFonts")) + InstallLabel.add_theme_stylebox_override(&"normal", get_theme_stylebox(&"bg", &"AssetLib")) + + InstallLabel.text = InstallLabel.text.replace(&"%%version%%", RivetPluginBridge.get_plugin().cli.REQUIRED_RIVET_CLI_VERSION).replace(&"%%bin_dir%%", RivetPluginBridge.get_plugin().cli.get_bin_dir()) + InstallButton.loading = true + var error = await RivetPluginBridge.get_plugin().cli.check_existence() + if error: + InstallButton.loading = false + return + owner.change_current_screen(owner.Screen.Login) + +func _ready() -> void: + InstallButton.pressed.connect(_on_install_button_pressed) + +func _on_install_button_pressed() -> void: + InstallButton.loading = true + var result = await RivetPluginBridge.get_plugin().cli.install() + if result.exit_code == 0: + var error = await RivetPluginBridge.get_plugin().cli.check_existence() + if not error: + InstallDialog.title = &"Success!" + InstallDialog.dialog_text = &"Rivet installed successfully!\nInstalled Rivet %s in %s" % [RivetPluginBridge.get_plugin().cli.REQUIRED_RIVET_CLI_VERSION, RivetPluginBridge.get_plugin().cli.get_bin_dir()] + InstallDialog.popup_centered() + owner.change_current_screen(owner.Screen.Login) + return + InstallDialog.title = &"Error!" + InstallDialog.dialog_text = &"Rivet installation failed! Please try again.\n\n%s" % result.output + InstallDialog.popup_centered() + InstallButton.loading = false \ No newline at end of file diff --git a/godot/bomber/addons/rivet/devtools/dock/installer.tscn b/godot/bomber/addons/rivet/devtools/dock/installer.tscn new file mode 100644 index 0000000..de1a031 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/installer.tscn @@ -0,0 +1,53 @@ +[gd_scene load_steps=5 format=3 uid="uid://d3l0arylk0h43"] + +[ext_resource type="PackedScene" uid="uid://dldxcm1l8nnnf" path="res://addons/rivet/devtools/dock/elements/logo_container.tscn" id="1_nj27r"] +[ext_resource type="Script" path="res://addons/rivet/devtools/dock/installer.gd" id="1_s8aji"] +[ext_resource type="PackedScene" uid="uid://bk1uwgw1hhq2p" path="res://addons/rivet/devtools/dock/elements/links_container.tscn" id="2_rgtqq"] +[ext_resource type="PackedScene" uid="uid://cdad7w76me3eu" path="res://addons/rivet/devtools/dock/elements/loading_button.tscn" id="4_ahrlb"] + +[node name="Installer" type="VBoxContainer"] +script = ExtResource("1_s8aji") + +[node name="LogoContainer" parent="." instance=ExtResource("1_nj27r")] +layout_mode = 2 + +[node name="HSeparator" type="HSeparator" parent="."] +layout_mode = 2 + +[node name="LinksContainer" parent="." instance=ExtResource("2_rgtqq")] +layout_mode = 2 + +[node name="CenterContainer" type="CenterContainer" parent="."] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer"] +layout_mode = 2 +theme_override_constants/separation = 16 +alignment = 1 + +[node name="InstallLabel" type="RichTextLabel" parent="CenterContainer/VBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(300, 2.08165e-12) +layout_mode = 2 +bbcode_enabled = true +text = "[center]Looks like you don't have installed Rivet CLI + +[b]Required[/b] [code]%%version%%[/code] in [code]%%bin_dir%%[/code]. + +In order to use this plugin you need to install it. Use button below to auto-install it.[/center]" +fit_content = true +scroll_active = false +autowrap_mode = 2 +deselect_on_focus_loss_enabled = false +drag_and_drop_selection_enabled = false + +[node name="InstallButton" parent="CenterContainer/VBoxContainer" instance=ExtResource("4_ahrlb")] +unique_name_in_owner = true +layout_mode = 2 +mouse_default_cursor_shape = 2 +text = "Install" + +[node name="InstallDialog" type="AcceptDialog" parent="."] +unique_name_in_owner = true +size = Vector2i(112, 100) diff --git a/godot/bomber/addons/rivet/devtools/dock/loading.gd b/godot/bomber/addons/rivet/devtools/dock/loading.gd new file mode 100644 index 0000000..ab2136e --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/loading.gd @@ -0,0 +1,9 @@ +extends VBoxContainer + + +func _ready() -> void: + %CancelButton.pressed.connect(_on_cancel_button_pressed) + +func _on_cancel_button_pressed() -> void: + # TODO(forest): cancel cli command + owner.change_current_screen(owner.Screen.Login) diff --git a/godot/bomber/addons/rivet/devtools/dock/loading.tscn b/godot/bomber/addons/rivet/devtools/dock/loading.tscn new file mode 100644 index 0000000..f111cd9 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/loading.tscn @@ -0,0 +1,37 @@ +[gd_scene load_steps=4 format=3 uid="uid://cpiafwq88eamc"] + +[ext_resource type="Script" path="res://addons/rivet/devtools/dock/loading.gd" id="1_2ygnd"] +[ext_resource type="PackedScene" uid="uid://dldxcm1l8nnnf" path="res://addons/rivet/devtools/dock/elements/logo_container.tscn" id="1_oda17"] +[ext_resource type="PackedScene" uid="uid://bk1uwgw1hhq2p" path="res://addons/rivet/devtools/dock/elements/links_container.tscn" id="2_c2osa"] + +[node name="Loading" type="VBoxContainer"] +theme_override_constants/separation = 16 +script = ExtResource("1_2ygnd") + +[node name="LogoContainer" parent="." instance=ExtResource("1_oda17")] +layout_mode = 2 + +[node name="HSeparator" type="HSeparator" parent="."] +layout_mode = 2 + +[node name="LinksContainer" parent="." instance=ExtResource("2_c2osa")] +layout_mode = 2 + +[node name="CenterContainer" type="CenterContainer" parent="."] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer"] +layout_mode = 2 +theme_override_constants/separation = 16 + +[node name="Label" type="Label" parent="CenterContainer/VBoxContainer"] +layout_mode = 2 +text = "Loading..." +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="CancelButton" type="Button" parent="CenterContainer/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Cancel" diff --git a/godot/bomber/addons/rivet/devtools/dock/login.gd b/godot/bomber/addons/rivet/devtools/dock/login.gd new file mode 100644 index 0000000..7a29fa4 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/login.gd @@ -0,0 +1,62 @@ +@tool extends Control +## A button that logs the user in to the Rivet using Rivet CLI. + +@onready var log_in_button: Button = %LogInButton +@onready var api_endpoint_line_edit: LineEdit = %ApiEndpointLineEdit +@onready var advanced_options_button: Button = %AdvancedOptionsButton +@onready var api_endpoint_field: Control = %ApiEndpointField + +func prepare() -> void: + var result = await RivetPluginBridge.get_plugin().cli.run_command([ + "sidekick", + "check-login-state", + ]) + if result.exit_code == result.ExitCode.SUCCESS and "Ok" in result.output: + owner.change_current_screen(owner.Screen.Settings) + return + +func _ready(): + log_in_button.pressed.connect(_on_button_pressed) + advanced_options_button.pressed.connect(_on_advanced_options_button_pressed) + advanced_options_button.icon = get_theme_icon("arrow", "OptionButton") + +func _on_button_pressed() -> void: + log_in_button.disabled = true + var api_address = api_endpoint_line_edit.text + var result = await RivetPluginBridge.get_plugin().cli.run_command([ + "--api-endpoint", + api_address, + "sidekick", + "get-link", + ]) + if result.exit_code != result.ExitCode.SUCCESS or !("Ok" in result.output): + RivetPluginBridge.display_cli_error(self, result) + log_in_button.disabled = false + return + var data: Dictionary = result.output["Ok"] + + # Now that we have the link, open it in the user's browser + OS.shell_open(data["device_link_url"]) + + owner.change_current_screen(owner.Screen.Loading) + + # Long-poll the Rivet API until the user has logged in + result = await RivetPluginBridge.get_plugin().cli.run_command([ + "--api-endpoint", + api_address, + "sidekick", + "wait-for-login", + "--device-link-token", + data["device_link_token"], + ]) + + if result.exit_code != result.ExitCode.SUCCESS or !("Ok" in result.output): + RivetPluginBridge.display_cli_error(self, result) + log_in_button.disabled = false + return + + log_in_button.disabled = false + owner.change_current_screen(owner.Screen.Settings) + +func _on_advanced_options_button_pressed(): + api_endpoint_field.visible = !api_endpoint_field.visible diff --git a/godot/bomber/addons/rivet/devtools/dock/login.tscn b/godot/bomber/addons/rivet/devtools/dock/login.tscn new file mode 100644 index 0000000..0f3eacd --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/login.tscn @@ -0,0 +1,63 @@ +[gd_scene load_steps=6 format=3 uid="uid://mag2n5yvyus8"] + +[ext_resource type="Script" path="res://addons/rivet/devtools/dock/login.gd" id="1_spqru"] +[ext_resource type="PackedScene" uid="uid://dldxcm1l8nnnf" path="res://addons/rivet/devtools/dock/elements/logo_container.tscn" id="2_qku2v"] +[ext_resource type="PackedScene" uid="uid://bk1uwgw1hhq2p" path="res://addons/rivet/devtools/dock/elements/links_container.tscn" id="3_cecsx"] + +[sub_resource type="Image" id="Image_w2r7k"] +data = { +"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 179, 179, 179, 153, 178, 178, 178, 166, 184, 184, 184, 18, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 184, 184, 184, 18, 178, 178, 178, 166, 178, 178, 178, 151, 255, 255, 255, 0, 255, 255, 255, 0, 178, 178, 178, 166, 178, 178, 178, 217, 178, 178, 178, 179, 184, 184, 184, 18, 255, 255, 255, 0, 255, 255, 255, 0, 175, 175, 175, 19, 178, 178, 178, 179, 178, 178, 178, 217, 178, 178, 178, 165, 255, 255, 255, 0, 255, 255, 255, 0, 184, 184, 184, 18, 177, 177, 177, 179, 178, 178, 178, 217, 179, 179, 179, 180, 175, 175, 175, 19, 175, 175, 175, 19, 179, 179, 179, 180, 178, 178, 178, 217, 177, 177, 177, 179, 180, 180, 180, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 184, 184, 184, 18, 177, 177, 177, 179, 178, 178, 178, 217, 178, 178, 178, 181, 178, 178, 178, 181, 178, 178, 178, 217, 177, 177, 177, 179, 180, 180, 180, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 184, 184, 184, 18, 177, 177, 177, 179, 178, 178, 178, 217, 178, 178, 178, 217, 177, 177, 177, 179, 180, 180, 180, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 184, 184, 184, 18, 178, 178, 178, 165, 178, 178, 178, 165, 180, 180, 180, 17, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0), +"format": "RGBA8", +"height": 12, +"mipmaps": false, +"width": 12 +} + +[sub_resource type="ImageTexture" id="ImageTexture_hy2yt"] +image = SubResource("Image_w2r7k") + +[node name="Login" type="VBoxContainer"] +theme_override_constants/separation = 16 +script = ExtResource("1_spqru") + +[node name="LogoContainer" parent="." instance=ExtResource("2_qku2v")] +layout_mode = 2 + +[node name="HSeparator" type="HSeparator" parent="."] +layout_mode = 2 + +[node name="LinksContainer" parent="." instance=ExtResource("3_cecsx")] +layout_mode = 2 + +[node name="LogInButton" type="Button" parent="."] +unique_name_in_owner = true +layout_mode = 2 +mouse_default_cursor_shape = 2 +text = "Sign in to Rivet" + +[node name="AdvancedOptionsButton" type="Button" parent="."] +unique_name_in_owner = true +layout_mode = 2 +text = "Advanced options" +icon = SubResource("ImageTexture_hy2yt") +flat = true +alignment = 0 +icon_alignment = 2 + +[node name="ApiEndpointField" type="VBoxContainer" parent="."] +unique_name_in_owner = true +visible = false +layout_mode = 2 + +[node name="Label" type="Label" parent="ApiEndpointField"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "API endpoint" + +[node name="ApiEndpointLineEdit" type="LineEdit" parent="ApiEndpointField"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "https://api.rivet.gg" +middle_mouse_paste_enabled = false +drag_and_drop_selection_enabled = false diff --git a/godot/bomber/addons/rivet/devtools/dock/playtest_tab.gd b/godot/bomber/addons/rivet/devtools/dock/playtest_tab.gd new file mode 100644 index 0000000..f00f5d4 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/playtest_tab.gd @@ -0,0 +1,113 @@ +@tool extends MarginContainer + +const ButtonsBar = preload("elements/buttons_bar.gd") + +@onready var namespace_description: RichTextLabel = %NamespaceDescription +@onready var buttons_bar: ButtonsBar = %ButtonsBar +@onready var warning: RichTextLabel = %WarningLabel +@onready var error: RichTextLabel = %ErrorLabel +@onready var deploy_button: Button = %DeployButton +@onready var namespace_selector = %AuthNamespaceSelector + +func _ready() -> void: + if get_tree().edited_scene_root == self: + return # This is the scene opened in the editor! + namespace_description.add_theme_font_override(&"mono_font", get_theme_font(&"output_source_mono", &"EditorFonts")) + namespace_description.add_theme_font_override(&"bold_font", get_theme_font(&"bold", &"EditorFonts")) + namespace_description.add_theme_stylebox_override(&"normal", get_theme_stylebox(&"bg", &"AssetLib")) + namespace_description.meta_clicked.connect(func(meta): OS.shell_open(str(meta))) + + warning.add_theme_color_override(&"default_color", get_theme_color(&"warning_color", &"Editor")) + warning.add_theme_stylebox_override(&"normal", get_theme_stylebox(&"bg", &"AssetLib")) + var warning_text = warning.text + warning.text = "" + warning.add_image(get_theme_icon("StatusWarning", "EditorIcons")) + warning.add_text(warning_text) + + error.add_theme_color_override(&"default_color", get_theme_color("error_color", "Editor")) + error.add_theme_stylebox_override(&"normal", get_theme_stylebox(&"bg", &"AssetLib")) + var error_text = error.text + error.text = "" + error.add_image(get_theme_icon("StatusError", "EditorIcons")) + error.add_text(error_text) + + warning.visible = false + error.visible = false + deploy_button.visible = false + + RivetPluginBridge.instance.bootstrapped.connect(_on_bootstrapped) + namespace_selector.item_selected.connect(_on_namespace_selector_item_selected) + deploy_button.pressed.connect(_on_deploy_button_pressed) + buttons_bar.selected.connect(_on_buttons_bar_selected) + +func _on_namespace_selector_item_selected(id: int) -> void: + _update_warnings() + +func _on_buttons_bar_selected() -> void: + _update_warnings() + +func _on_bootstrapped() -> void: + _update_warnings() + +func _update_warnings() -> void: + var is_local_machine = buttons_bar.current == 0 + var is_online_server = buttons_bar.current == 1 + var current_namespace = namespace_selector.current_value + + # Local machine + if is_local_machine: + warning.visible = false + error.visible = false + deploy_button.visible = false + _generate_dev_auth_token(current_namespace) + return + + # Online server + if is_online_server: + # It means that user hasn't deployed anything to this namespace yet + if current_namespace.version.display_name == "0.0.1": + warning.visible = false + error.visible = true + deploy_button.visible = true + else: + warning.visible = true + error.visible = false + deploy_button.visible = false + _generate_public_auth_token(current_namespace) + return + +func _all_actions_set_disabled(disabled: bool) -> void: + namespace_selector.disabled = disabled + buttons_bar.disabled = disabled + +func _generate_dev_auth_token(ns) -> void: + _actions_disabled_while(func(): + var result = await RivetPluginBridge.get_plugin().cli.run_command(["sidekick", "get-namespace-development-token", "--namespace", ns.name_id]) + if result.exit_code != 0 or !("Ok" in result.output): + RivetPluginBridge.display_cli_error(self, result) + return + + RivetPluginBridge.get_plugin().namespace_token = result.output["Ok"]["token"] + RivetPluginBridge.instance.save_configuration() + ) + +func _generate_public_auth_token(ns) -> void: + _actions_disabled_while(func(): + var result = await RivetPluginBridge.get_plugin().cli.run_command(["sidekick", "get-namespace-public-token", "--namespace", ns.name_id]) + if result.exit_code != 0 or !("Ok" in result.output): + RivetPluginBridge.display_cli_error(self, result) + return + + RivetPluginBridge.get_plugin().namespace_token = result.output["Ok"]["token"] + RivetPluginBridge.instance.save_configuration() + ) + +func _actions_disabled_while(fn: Callable) -> void: + _all_actions_set_disabled(true) + await fn.call() + _all_actions_set_disabled(false) + +func _on_deploy_button_pressed() -> void: + owner.change_tab(1) + owner.deploy_tab.namespace_selector.current_value = namespace_selector.current_value + owner.deploy_tab.namespace_selector.selected = namespace_selector.selected diff --git a/godot/bomber/addons/rivet/devtools/dock/playtest_tab.tscn b/godot/bomber/addons/rivet/devtools/dock/playtest_tab.tscn new file mode 100644 index 0000000..e8881e9 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/playtest_tab.tscn @@ -0,0 +1,72 @@ +[gd_scene load_steps=4 format=3 uid="uid://b17eqs0bmrncs"] + +[ext_resource type="Script" path="res://addons/rivet/devtools/dock/playtest_tab.gd" id="1_4p5p5"] +[ext_resource type="PackedScene" uid="uid://bogw8dj8rr202" path="res://addons/rivet/devtools/dock/elements/namespace_menu_button.tscn" id="1_brqcl"] +[ext_resource type="PackedScene" uid="uid://bdpu38hakasqq" path="res://addons/rivet/devtools/dock/elements/buttons_bar.tscn" id="2_hd7vo"] + +[node name="Playtest" type="MarginContainer"] +script = ExtResource("1_4p5p5") + +[node name="PlaytestContent" type="VBoxContainer" parent="."] +layout_mode = 2 + +[node name="ButtonsBar" parent="PlaytestContent" instance=ExtResource("2_hd7vo")] +unique_name_in_owner = true +layout_mode = 2 +alignment = 1 + +[node name="ThisMachineButton" type="Button" parent="PlaytestContent/ButtonsBar"] +layout_mode = 2 +tooltip_text = "Test against your local machine to iterate quickly in the editor." +mouse_default_cursor_shape = 2 +toggle_mode = true +button_pressed = true +text = "This machine" + +[node name="RivetServersButton" type="Button" parent="PlaytestContent/ButtonsBar"] +layout_mode = 2 +tooltip_text = "Deploy your game to Rivet's servers to playtest with others." +mouse_default_cursor_shape = 2 +toggle_mode = true +text = "Rivet servers" + +[node name="VBoxContainer" type="VBoxContainer" parent="PlaytestContent"] +layout_mode = 2 + +[node name="Namespace" type="Label" parent="PlaytestContent/VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "Namespace" + +[node name="AuthNamespaceSelector" parent="PlaytestContent/VBoxContainer" instance=ExtResource("1_brqcl")] +layout_mode = 2 + +[node name="NamespaceDescription" type="RichTextLabel" parent="PlaytestContent/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +bbcode_enabled = true +text = "Configure which configuration should be emulated. [url=https://rivet.gg/docs/general/concepts/dev-tokens]Learn more...[/url]" +fit_content = true +scroll_active = false +autowrap_mode = 2 + +[node name="WarningLabel" type="RichTextLabel" parent="PlaytestContent/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = " Please make sure that you’re using the same version that’s deployed. +Using different versions can cause some unforeseen issues." +fit_content = true +scroll_active = false + +[node name="ErrorLabel" type="RichTextLabel" parent="PlaytestContent/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = " Before you can test online, you need to deploy your current game version to Rivet. +In order to deploy current game version to the cloud, click the button below." +fit_content = true +scroll_active = false + +[node name="DeployButton" type="Button" parent="PlaytestContent/VBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +text = "Deploy" diff --git a/godot/bomber/addons/rivet/devtools/dock/settings.gd b/godot/bomber/addons/rivet/devtools/dock/settings.gd new file mode 100644 index 0000000..3ff0aa1 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/settings.gd @@ -0,0 +1,15 @@ +@tool extends Control +## Settings screens allow you to configure and deploy your game. + +@onready var errorDialog: AcceptDialog = %ErrorDialog +@onready var buttons_bar: HBoxContainer = %ButtonsBar +@onready var deploy_tab = %Deploy + +func prepare(): + var error = await RivetPluginBridge.instance.bootstrap() + if error: + errorDialog.popup_centered() + return + +func change_tab(tab: int): + buttons_bar.set_current_button(tab) \ No newline at end of file diff --git a/godot/bomber/addons/rivet/devtools/dock/settings.tscn b/godot/bomber/addons/rivet/devtools/dock/settings.tscn new file mode 100644 index 0000000..e409822 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/settings.tscn @@ -0,0 +1,73 @@ +[gd_scene load_steps=8 format=3 uid="uid://ceovepvn1782o"] + +[ext_resource type="PackedScene" uid="uid://dldxcm1l8nnnf" path="res://addons/rivet/devtools/dock/elements/logo_container.tscn" id="1_22gtj"] +[ext_resource type="Script" path="res://addons/rivet/devtools/dock/settings.gd" id="1_qd6no"] +[ext_resource type="PackedScene" uid="uid://bk1uwgw1hhq2p" path="res://addons/rivet/devtools/dock/elements/links_container.tscn" id="2_mervt"] +[ext_resource type="PackedScene" uid="uid://b17eqs0bmrncs" path="res://addons/rivet/devtools/dock/playtest_tab.tscn" id="4_e3aw6"] +[ext_resource type="PackedScene" uid="uid://bdpu38hakasqq" path="res://addons/rivet/devtools/dock/elements/buttons_bar.tscn" id="4_urbnf"] +[ext_resource type="PackedScene" uid="uid://dxoly5h64g3ul" path="res://addons/rivet/devtools/dock/settings_tab.tscn" id="6_fu2a3"] +[ext_resource type="PackedScene" uid="uid://soum1c8oyrso" path="res://addons/rivet/devtools/dock/deploy_tab.tscn" id="6_y2fod"] + +[node name="Settings" type="VBoxContainer"] +theme_override_constants/separation = 16 +script = ExtResource("1_qd6no") + +[node name="LogoContainer" parent="." instance=ExtResource("1_22gtj")] +layout_mode = 2 + +[node name="HSeparator" type="HSeparator" parent="."] +layout_mode = 2 + +[node name="LinksContainer" parent="." instance=ExtResource("2_mervt")] +layout_mode = 2 + +[node name="ErrorDialog" type="AcceptDialog" parent="."] +unique_name_in_owner = true +size = Vector2i(112, 100) +dialog_text = "Configuration for this project couldn't be fetched. Have you run rivet init?" + +[node name="ButtonsBar" parent="." node_paths=PackedStringArray("tab_container") instance=ExtResource("4_urbnf")] +unique_name_in_owner = true +layout_mode = 2 +tab_container = NodePath("../TabContainer") + +[node name="PlaytestButton" type="Button" parent="ButtonsBar"] +layout_mode = 2 +size_flags_horizontal = 3 +mouse_default_cursor_shape = 2 +toggle_mode = true +button_pressed = true +text = "Playtest" + +[node name="DeployButton" type="Button" parent="ButtonsBar"] +layout_mode = 2 +size_flags_horizontal = 3 +mouse_default_cursor_shape = 2 +toggle_mode = true +text = "Deploy" + +[node name="SettingsButton" type="Button" parent="ButtonsBar"] +layout_mode = 2 +size_flags_horizontal = 3 +mouse_default_cursor_shape = 2 +toggle_mode = true +text = "Settings" + +[node name="TabContainer" type="TabContainer" parent="."] +unique_name_in_owner = true +layout_mode = 2 +tabs_visible = false + +[node name="Playtest" parent="TabContainer" instance=ExtResource("4_e3aw6")] +unique_name_in_owner = true +layout_mode = 2 + +[node name="Deploy" parent="TabContainer" instance=ExtResource("6_y2fod")] +unique_name_in_owner = true +visible = false +layout_mode = 2 + +[node name="Settings" parent="TabContainer" instance=ExtResource("6_fu2a3")] +unique_name_in_owner = true +visible = false +layout_mode = 2 diff --git a/godot/bomber/addons/rivet/devtools/dock/settings_tab.gd b/godot/bomber/addons/rivet/devtools/dock/settings_tab.gd new file mode 100644 index 0000000..cd0660d --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/settings_tab.gd @@ -0,0 +1,24 @@ +@tool extends Control + +@onready var unlink_game_button: Button = %UnlinkGameButton + + +func _ready() -> void: + unlink_game_button.pressed.connect(_on_unlink_game_button_pressed) + + +func _on_unlink_game_button_pressed() -> void: + unlink_game_button.disabled = true + + var result = await RivetPluginBridge.get_plugin().cli.run_command([ + "unlink" + ]) + + if result.exit_code != result.ExitCode.SUCCESS: + RivetPluginBridge.display_cli_error(self, result) + unlink_game_button.disabled = false + return + + unlink_game_button.disabled = false + owner.owner.reload() + owner.owner.change_current_screen(owner.owner.Screen.Login) \ No newline at end of file diff --git a/godot/bomber/addons/rivet/devtools/dock/settings_tab.tscn b/godot/bomber/addons/rivet/devtools/dock/settings_tab.tscn new file mode 100644 index 0000000..05f4bc0 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/dock/settings_tab.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=2 format=3 uid="uid://dxoly5h64g3ul"] + +[ext_resource type="Script" path="res://addons/rivet/devtools/dock/settings_tab.gd" id="1_qo1d8"] + +[node name="Settings" type="MarginContainer"] +script = ExtResource("1_qo1d8") + +[node name="UnlinkGameButton" type="Button" parent="."] +unique_name_in_owner = true +layout_mode = 2 +mouse_default_cursor_shape = 2 +text = "Unlink game" diff --git a/godot/bomber/addons/rivet/devtools/rivet_cli.gd b/godot/bomber/addons/rivet/devtools/rivet_cli.gd new file mode 100644 index 0000000..3be050c --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/rivet_cli.gd @@ -0,0 +1,76 @@ +extends RefCounted +## Wrapper aroudn the Rivet CLI, allowing you to run it from GDScript in non-blocking way, and get the output. +## +## @experimental + +const REQUIRED_RIVET_CLI_VERSION = "v1.0.0" + +const _RivetEditorSettings = preload("rivet_editor_settings.gd") +const _RivetThread = preload("rivet_thread.gd") +const _RivetCliOutput = preload("rivet_cli_output.gd") + +func check_existence() -> Error: + var editor_rivet_path = _RivetEditorSettings.get_setting(_RivetEditorSettings.RIVET_CLI_PATH_SETTING.name) + if not editor_rivet_path or editor_rivet_path.is_empty(): + return FAILED + var result: _RivetCliOutput = await run_command(["sidekick", "get-cli-version"]) + if result.exit_code != 0 or !("Ok" in result.output): + return FAILED + var cli_version = result.output["Ok"].version + if cli_version != REQUIRED_RIVET_CLI_VERSION: + return FAILED + return OK + +func run_command(args: PackedStringArray) -> _RivetCliOutput: + var thread: _RivetThread = _RivetThread.new(_run.bind(args)) + return await thread.wait_to_finish() + +func get_bin_dir() -> String: + var home_path: String = OS.get_environment("HOME") + return home_path.path_join(".rivet").path_join(REQUIRED_RIVET_CLI_VERSION).path_join("bin") + +func get_cli_path() -> String: + var cli_path = _RivetEditorSettings.get_setting(_RivetEditorSettings.RIVET_CLI_PATH_SETTING.name) + if cli_path and !cli_path.is_empty(): + return cli_path + return get_bin_dir().path_join("rivet.exe" if OS.get_name() == "Windows" else "rivet") + +func install() -> _RivetCliOutput: + var thread: _RivetThread = _RivetThread.new(_install) + var result = await thread.wait_to_finish() + if result.exit_code == 0: + _RivetEditorSettings.set_setting_value(_RivetEditorSettings.RIVET_CLI_PATH_SETTING.name, get_bin_dir()) + return result + + +## region Internal functions + +## Runs Rivet CLI with given arguments. +func _run(args: PackedStringArray) -> _RivetCliOutput: + var output = [] + RivetPluginBridge.log(["Running Rivet CLI: ", "%s %s" % [get_cli_path(), " ".join(args)]]) + var code: int = OS.execute(get_cli_path(), args, output, true) + + return _RivetCliOutput.new(code, output) + +func _install() -> _RivetCliOutput: + var output = [] + var code: int + var bin_dir: String = get_bin_dir() + + OS.set_environment("RIVET_CLI_VERSION", REQUIRED_RIVET_CLI_VERSION) + OS.set_environment("BIN_DIR", bin_dir) + + # Double quotes issue: https://github.com/godotengine/godot/issues/37291#issuecomment-603821838 + if OS.get_name() == "Windows": + var args = ["-Commandi", "\"'iwr https://raw.githubusercontent.com/rivet-gg/cli/$env:RIVET_CLI_VERSION/install/windows.ps1 -useb | iex'\""] + code = OS.execute("powershell.exe", args, output, true, true) + else: + #var args = ["-c", "\"'curl -fsSL https://raw.githubusercontent.com/rivet-gg/cli/${RIVET_CLI_VERSION}/install/unix.sh | sh''\""] + var args = ["-c", "\"'curl -fsSL https://raw.githubusercontent.com/rivet-gg/cli/ac57796861d195230fa043e12c5f9fe1921f467f/install/unix.sh | sh'\""] + code = OS.execute("/bin/sh", args, output, true, true) + return _RivetCliOutput.new(code, output) + + + +## endregion \ No newline at end of file diff --git a/godot/bomber/addons/rivet/devtools/rivet_cli_output.gd b/godot/bomber/addons/rivet/devtools/rivet_cli_output.gd new file mode 100644 index 0000000..889499e --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/rivet_cli_output.gd @@ -0,0 +1,38 @@ +extends RefCounted +## It's a wrapper for the output of the command line tools + +var exit_code: ExitCode +var output: Dictionary + +## The exit code of the command line tool +enum ExitCode { + SUCCESS = 0 + # TODO: fill with the rest of the exit codes +} + +func _init(exit_code: int, internal_output: Array) -> void: + self.exit_code = exit_code + + if internal_output and not internal_output.is_empty(): + _parse_output(internal_output) + +func _parse_output(internal_output: Array) -> void: + var lines_with_json = internal_output.filter( + func (line: String): + return line.find("{") != -1 + ) + + if lines_with_json.is_empty(): + print("No JSON output found") + return + + var line_with_json: String = lines_with_json.front() + # Parse the output as JSON + var json: JSON = JSON.new() + var error: Error = json.parse(line_with_json) + + if error == OK: + self.output = json.data + else: + # If the output is not JSON, throw an error + RivetPluginBridge.error("Invalid response from the command line tool: " + str(error)) diff --git a/godot/bomber/addons/rivet/devtools/rivet_editor_settings.gd b/godot/bomber/addons/rivet/devtools/rivet_editor_settings.gd new file mode 100644 index 0000000..89196c1 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/rivet_editor_settings.gd @@ -0,0 +1,29 @@ +const RIVET_CLI_PATH_SETTING = { + "name": "rivet/cli_executable_path", + "type": TYPE_STRING, + "hint": PROPERTY_HINT_TYPE_STRING, +} +const RIVET_DEBUG_SETTING ={ + "name": "rivet/debug", + "type": TYPE_BOOL, +} + +## Returns the path to the Rivet CLI executable stored in the editor settings. +static func set_defaults(settings: EditorSettings = EditorInterface.get_editor_settings()) -> void: + set_default_setting_value(RIVET_CLI_PATH_SETTING["name"], "", settings) + settings.add_property_info(RIVET_CLI_PATH_SETTING) + set_default_setting_value(RIVET_DEBUG_SETTING["name"], false, settings) + settings.add_property_info(RIVET_DEBUG_SETTING) + +## Sets the path to the Rivet CLI executable in the editor settings, if it is not already set. +static func set_default_setting_value(name: String, default_value: Variant, settings: EditorSettings = EditorInterface.get_editor_settings()) -> void: + var existing_value = settings.get_setting(name) + settings.set_initial_value(name, default_value, false) + settings.set_setting(name, existing_value if existing_value else default_value) + +static func set_setting_value(name: String, value: Variant, settings: EditorSettings = EditorInterface.get_editor_settings()) -> void: + settings.set_setting(name, value) + +## Returns the path to the Rivet CLI executable stored in the editor settings. +static func get_setting(name: String, settings: EditorSettings = EditorInterface.get_editor_settings()) -> Variant: + return settings.get_setting(name) diff --git a/godot/bomber/addons/rivet/devtools/rivet_export_plugin.gd b/godot/bomber/addons/rivet/devtools/rivet_export_plugin.gd new file mode 100644 index 0000000..bbb7419 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/rivet_export_plugin.gd @@ -0,0 +1,29 @@ +@tool +extends EditorExportPlugin + +var has_added_file: bool = false +var _plugin_name = "RivetEditorPlugin" + +func _supports_platform(platform): + return true + +func _get_name(): + return _plugin_name + +func _export_begin(features: PackedStringArray, is_debug: bool, path: String, flags: int) -> void: + if not FileAccess.file_exists(RivetPluginBridge.RIVET_CONFIGURATION_FILE_PATH): + push_warning("Rivet plugin not configured. Please configure it using plugin interface.") + return + + var configuration_file = FileAccess.open(RivetPluginBridge.RIVET_CONFIGURATION_FILE_PATH, FileAccess.READ) + var source = configuration_file.get_as_text() + var script = GDScript.new() + script.source_code = source + var error: Error = ResourceSaver.save(script, RivetPluginBridge.RIVET_DEPLOYED_CONFIGURATION_FILE_PATH) + if not error: + has_added_file = true + + +func _export_end() -> void: + if has_added_file: + DirAccess.remove_absolute(RivetPluginBridge.RIVET_DEPLOYED_CONFIGURATION_FILE_PATH) \ No newline at end of file diff --git a/godot/bomber/addons/rivet/devtools/rivet_plugin_bridge.gd b/godot/bomber/addons/rivet/devtools/rivet_plugin_bridge.gd new file mode 100644 index 0000000..018e3b3 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/rivet_plugin_bridge.gd @@ -0,0 +1,128 @@ +@tool class_name RivetPluginBridge +## Scaffolding for the plugin to be used in the editor, this is not meant to be +## used in the game. It's a way to get the plugin instance from the engine's +## perspective. +## +## @experimental + +signal bootstrapped + +const RIVET_CONFIGURATION_PATH: String = "res://.rivet" +const RIVET_CONFIGURATION_FILE_PATH: String = "res://.rivet/config.gd" +const RIVET_DEPLOYED_CONFIGURATION_FILE_PATH: String = "res://.rivet_config.gd" +const SCRIPT_TEMPLATE: String = """ +extends RefCounted +const api_endpoint: String = \"{api_endpoint}\" +const namespace_token: String = \"{namespace_token}\" +const cloud_token: String = \"{cloud_token}\" +const game_id: String = \"{game_id}\" +""" +const _global := preload("../rivet_global.gd") +const _RivetEditorSettings = preload("./rivet_editor_settings.gd") + +static var game_namespaces: Array + +static var instance = RivetPluginBridge.new() + +static func _find_plugin(): + var tree: SceneTree = Engine.get_main_loop() + return tree.get_root().get_child(0).get_node_or_null("RivetPlugin") + +static func display_cli_error(node: Node, cli_output) -> AcceptDialog: + var error = cli_output.output["Err"].c_unescape() if "Err" in cli_output.output else "\n".join(cli_output.formatted_output) + var alert = AcceptDialog.new() + alert.title = "Error!" + alert.dialog_text = error + alert.dialog_autowrap = true + alert.close_requested.connect(func(): alert.queue_free() ) + node.add_child(alert) + alert.popup_centered_ratio(0.4) + return alert + +# https://github.com/godotengine/godot-proposals/issues/900#issuecomment-1812881718 +static func is_part_of_edited_scene(node: Node): + return Engine.is_editor_hint() && node.is_inside_tree() && node.get_tree().get_edited_scene_root() && (node.get_tree().get_edited_scene_root() == node || node.get_tree().get_edited_scene_root().is_ancestor_of(node)) + +## Autoload is not available for editor interfaces, we add a scoffolding to get +## the instance of the plugin from the engine's perspective +## @experimental +static func get_plugin() -> _global: + var plugin = _find_plugin() + if plugin: + return plugin.global + return null + +static func log(args): + if _RivetEditorSettings.get_setting(_RivetEditorSettings.RIVET_DEBUG_SETTING.name): + print("[Rivet] ", args) + +static func warning(args): + push_warning("[Rivet] ", args) + +static func error(args): + push_error("[Rivet] ", args) + +func save_configuration(): + DirAccess.make_dir_recursive_absolute(RIVET_CONFIGURATION_PATH) + + var gd_ignore_path = RIVET_CONFIGURATION_PATH.path_join(".gdignore") + if not FileAccess.file_exists(gd_ignore_path): + var gd_ignore = FileAccess.open(gd_ignore_path, FileAccess.WRITE) + gd_ignore.store_string("") + + var git_ignore_path = RIVET_CONFIGURATION_PATH.path_join(".gitignore") + if not FileAccess.file_exists(git_ignore_path): + var git_ignore = FileAccess.open(git_ignore_path, FileAccess.WRITE) + git_ignore.store_string("*") + + var plg = get_plugin() + var script: GDScript = GDScript.new() + script.source_code = SCRIPT_TEMPLATE.format({"api_endpoint": plg.api_endpoint, "namespace_token": plg.namespace_token, "cloud_token": plg.cloud_token, "game_id": plg.game_id}) + var err: Error = ResourceSaver.save(script, RIVET_CONFIGURATION_FILE_PATH) + if err: + push_warning("Error saving Rivet data: %s" % err) + +func bootstrap() -> Error: + var plugin = get_plugin() + if not plugin: + return FAILED + + var result = await get_plugin().cli.run_command([ + "sidekick", + "get-bootstrap-data", + ]) + + if result.exit_code != 0 or !("Ok" in result.output): + return FAILED + + get_plugin().api_endpoint = result.output["Ok"].api_endpoint + get_plugin().cloud_token = result.output["Ok"].token + get_plugin().game_id = result.output["Ok"].game_id + + save_configuration() + + var fetch_result = await _fetch_plugin_data() + if fetch_result == OK: + emit_signal("bootstrapped") + return fetch_result + +func _fetch_plugin_data() -> Error: + var response = await get_plugin().GET("/cloud/games/%s" % get_plugin().game_id).wait_completed() + # response.body: + # game.namespaces = {namespace_id, version_id, display_name}[] + # game.versions = {version_id, display_name}[] + if response.response_code != HTTPClient.ResponseCode.RESPONSE_OK: + return FAILED + + var namespaces = response.body.game.namespaces + for space in namespaces: + var versions: Array = response.body.game.versions.filter( + func (version): return version.version_id == space.version_id + ) + if versions.is_empty(): + space["version"] = null + else: + space["version"] = versions[0] + + game_namespaces = namespaces + return OK diff --git a/godot/bomber/addons/rivet/devtools/rivet_thread.gd b/godot/bomber/addons/rivet/devtools/rivet_thread.gd new file mode 100644 index 0000000..2f181f0 --- /dev/null +++ b/godot/bomber/addons/rivet/devtools/rivet_thread.gd @@ -0,0 +1,29 @@ +extends RefCounted +## A wrapper around Thread that allows you to wait for the thread to finish and get the result. +## +## @experimental + +signal finished(output: Variant) + +var _mutex: Mutex +var _thread: Thread + +## Result of the thread. +var output: Variant = null + +## Returns the output of the thread. +func wait_to_finish(): + await finished + return output + +func _init(fn: Callable) -> void: + _thread = Thread.new() + _mutex = Mutex.new() + _thread.start(func(): + var result = fn.call() + _mutex.lock() + output = result + call_deferred("emit_signal", "finished", result) + _mutex.unlock() + return result + ) diff --git a/godot/bomber/addons/rivet/images/icon-circle.png.import b/godot/bomber/addons/rivet/images/icon-circle.png.import new file mode 100644 index 0000000..5d95e42 --- /dev/null +++ b/godot/bomber/addons/rivet/images/icon-circle.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cle7km6xt44im" +path="res://.godot/imported/icon-circle.png-ad959504104d5f2d6c4a4ce568b03d7e.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/rivet/images/icon-circle.png" +dest_files=["res://.godot/imported/icon-circle.png-ad959504104d5f2d6c4a4ce568b03d7e.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/godot/bomber/addons/rivet/images/icon-text-black.svg b/godot/bomber/addons/rivet/images/icon-text-black.svg new file mode 100644 index 0000000..14f0fe0 --- /dev/null +++ b/godot/bomber/addons/rivet/images/icon-text-black.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/godot/bomber/addons/rivet/images/icon-text-black.svg.import b/godot/bomber/addons/rivet/images/icon-text-black.svg.import new file mode 100644 index 0000000..cdda90d --- /dev/null +++ b/godot/bomber/addons/rivet/images/icon-text-black.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://10vqh72x3wrr" +path="res://.godot/imported/icon-text-black.svg-10e6532df3787d7fde392c7ee5fc0deb.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/rivet/images/icon-text-black.svg" +dest_files=["res://.godot/imported/icon-text-black.svg-10e6532df3787d7fde392c7ee5fc0deb.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/bomber/addons/rivet/images/icon-text-white.svg b/godot/bomber/addons/rivet/images/icon-text-white.svg new file mode 100644 index 0000000..1807618 --- /dev/null +++ b/godot/bomber/addons/rivet/images/icon-text-white.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/godot/bomber/addons/rivet/images/icon-text-white.svg.import b/godot/bomber/addons/rivet/images/icon-text-white.svg.import new file mode 100644 index 0000000..cd17de1 --- /dev/null +++ b/godot/bomber/addons/rivet/images/icon-text-white.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://duc87c0hyoyqk" +path="res://.godot/imported/icon-text-white.svg-c0a5d8cecfa0530a176b0b7cc6c0fed1.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/rivet/images/icon-text-white.svg" +dest_files=["res://.godot/imported/icon-text-white.svg-c0a5d8cecfa0530a176b0b7cc6c0fed1.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/bomber/addons/rivet/images/white-logo.svg.import b/godot/bomber/addons/rivet/images/white-logo.svg.import new file mode 100644 index 0000000..d343b6c --- /dev/null +++ b/godot/bomber/addons/rivet/images/white-logo.svg.import @@ -0,0 +1,37 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cebc1tvicnoux" +path="res://.godot/imported/white-logo.svg-573e799fd39e8cb89bc8e36e6ad9780d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://addons/rivet/images/white-logo.svg" +dest_files=["res://.godot/imported/white-logo.svg-573e799fd39e8cb89bc8e36e6ad9780d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/godot/bomber/addons/rivet_api/plugin.cfg b/godot/bomber/addons/rivet/plugin.cfg similarity index 64% rename from godot/bomber/addons/rivet_api/plugin.cfg rename to godot/bomber/addons/rivet/plugin.cfg index fe1097e..79be32c 100644 --- a/godot/bomber/addons/rivet_api/plugin.cfg +++ b/godot/bomber/addons/rivet/plugin.cfg @@ -3,6 +3,6 @@ name="Rivet API" description="" author="Rivet Gaming, Inc." -version="0.0.1" -script="rivet_api.gd" +version="1.0.0-rc.1" +script="rivet.gd" diff --git a/godot/bomber/addons/rivet/rivet.gd b/godot/bomber/addons/rivet/rivet.gd new file mode 100644 index 0000000..db0d56c --- /dev/null +++ b/godot/bomber/addons/rivet/rivet.gd @@ -0,0 +1,58 @@ +@tool extends EditorPlugin +## Mainpoint for the Rivet editor plugin. + +# MARK: Plugin +const AUTO_LOAD_RIVET_CLIENT = "RivetClient" +const AUTO_LOAD_RIVET_HELPER = "RivetHelper" +const AUTO_LOAD_RIVET_GLOBAL = "Rivet" + +const _RivetEditorSettings := preload("devtools/rivet_editor_settings.gd") +const _RivetGlobal := preload("rivet_global.gd") +const _RivetCLI = preload("devtools/rivet_cli.gd") + +var _dock: Control +var _export_plugin: EditorExportPlugin +var cli: _RivetCLI = _RivetCLI.new() + +## The global singleton for the Rivet plugin, only available in the editor. +var global: _RivetGlobal + +func _init() -> void: + name = "RivetPlugin" + +func _enter_tree(): + # Add singleton + add_autoload_singleton(AUTO_LOAD_RIVET_CLIENT, "rivet_client.gd") + add_autoload_singleton(AUTO_LOAD_RIVET_HELPER, "rivet_helper.gd") + + add_autoload_singleton(AUTO_LOAD_RIVET_GLOBAL, "rivet_global.gd") + + global = _RivetGlobal.new() + global.cli = cli + + _dock = preload("devtools/dock/dock.tscn").instantiate() + _dock.add_child(global) + + # Add export plugin + _export_plugin = preload("devtools/rivet_export_plugin.gd").new() + add_export_plugin(_export_plugin) + + # Add dock + add_control_to_dock(DOCK_SLOT_LEFT_BR, _dock) + _RivetEditorSettings.set_defaults() + + +func _exit_tree(): + # Remove singleton + remove_autoload_singleton(AUTO_LOAD_RIVET_CLIENT) + remove_autoload_singleton(AUTO_LOAD_RIVET_HELPER) + remove_autoload_singleton(AUTO_LOAD_RIVET_GLOBAL) + + # Remove export plugin + remove_export_plugin(_export_plugin) + _export_plugin = null + + # Remove dock + remove_control_from_docks(_dock) + _dock.free() + diff --git a/godot/bomber/addons/rivet_api/rivet.toml.tpl b/godot/bomber/addons/rivet/rivet.version.toml.tpl similarity index 99% rename from godot/bomber/addons/rivet_api/rivet.toml.tpl rename to godot/bomber/addons/rivet/rivet.version.toml.tpl index ca1b0e3..0916519 100644 --- a/godot/bomber/addons/rivet_api/rivet.toml.tpl +++ b/godot/bomber/addons/rivet/rivet.version.toml.tpl @@ -1,5 +1,5 @@ # === Rivet Version Configuration === -# +# # - More info: https://docs.rivet.gg/general/concepts/rivet-version-config # - Reference: https://docs.rivet.gg/cloud/api/post-games-versions#body # - Publish a new version with `rivet publish` diff --git a/godot/bomber/addons/rivet_api/rivet_client.gd b/godot/bomber/addons/rivet/rivet_client.gd similarity index 93% rename from godot/bomber/addons/rivet_api/rivet_client.gd rename to godot/bomber/addons/rivet/rivet_client.gd index 6bb2ad7..207e094 100644 --- a/godot/bomber/addons/rivet_api/rivet_client.gd +++ b/godot/bomber/addons/rivet/rivet_client.gd @@ -1,21 +1,27 @@ +## @deprecated extends Node var base_url = "https://api.rivet.gg/v1" +## @deprecated func get_token(): var token_env = OS.get_environment("RIVET_TOKEN") assert(!token_env.is_empty(), "missing RIVET_TOKEN environment") return token_env - + +## @deprecated func lobby_ready(body: Variant, on_success: Callable, on_fail: Callable): _rivet_request_with_body("POST", "matchmaker", "/lobbies/ready", body, on_success, on_fail) +## @deprecated func find_lobby(body: Variant, on_success: Callable, on_fail: Callable): _rivet_request_with_body("POST", "matchmaker", "/lobbies/find", body, on_success, on_fail) +## @deprecated func player_connected(body: Variant, on_success: Callable, on_fail: Callable): _rivet_request_with_body("POST", "matchmaker", "/players/connected", body, on_success, on_fail) +## @deprecated func player_disconnected(body: Variant, on_success: Callable, on_fail: Callable): _rivet_request_with_body("POST", "matchmaker", "/players/disconnected", body, on_success, on_fail) @@ -27,6 +33,7 @@ func _build_headers() -> PackedStringArray: "Authorization: Bearer " + get_token(), ] +## @deprecated func _rivet_request(method: String, service: String, path: String, on_success: Callable, on_fail: Callable): var url = _build_url(service, path) RivetHelper.rivet_print("%s %s" % [method, url]) @@ -41,6 +48,7 @@ func _rivet_request(method: String, service: String, path: String, on_success: C if on_fail != null: on_fail.call("Request failed to send: %s" % error) +## @deprecated func _rivet_request_with_body(method: String, service: String, path: String, body: Variant, on_success: Callable, on_fail: Callable): var url = _build_url(service, path) RivetHelper.rivet_print("%s %s: %s" % [method, url, body]) @@ -56,12 +64,16 @@ func _rivet_request_with_body(method: String, service: String, path: String, bod if on_fail != null: on_fail.call("Request failed to send: %s" % error) +## @deprecated func _http_request_completed(result, response_code, _headers, body, on_success: Callable, on_fail: Callable): if result != HTTPRequest.RESULT_SUCCESS: push_error("Request error ", result) if on_fail != null: on_fail.call("Request error: %s" % result) return + + RivetHelper.rivet_print("%s: %s" % [response_code, body.get_string_from_utf8()]) + if response_code != 200: push_error("Request failed ", response_code, " ", body.get_string_from_utf8()) if on_fail != null: @@ -72,7 +84,6 @@ func _http_request_completed(result, response_code, _headers, body, on_success: json.parse(body.get_string_from_utf8()) var response = json.get_data() - RivetHelper.rivet_print("Success") if on_success != null: on_success.call(response) diff --git a/godot/bomber/addons/rivet/rivet_global.gd b/godot/bomber/addons/rivet/rivet_global.gd new file mode 100644 index 0000000..cfa63c2 --- /dev/null +++ b/godot/bomber/addons/rivet/rivet_global.gd @@ -0,0 +1,36 @@ + +extends Node +## Rivet [/br] +## Mainpoint of the Rivet plugin. +## It includes an easy access to APIs, helpers and tools. [/br] +## @tutorial: https://rivet.gg/learn/godot +## @experimental + +const _api = preload("api/rivet_api.gd") + +const ApiResponse = preload("api/rivet_response.gd") +const ApiRequest = preload("api/rivet_request.gd") + +const _Packages = preload("api/rivet_packages.gd") + +var cloud_token: String +var namespace_token: String +var game_id: String +var api_endpoint: String + +var matchmaker: _Packages.Matchmaker = _Packages.Matchmaker.new() + +# This variable is only accessible from editor's scripts, please do not use it in your game. +var cli + +## @experimental +func POST(path: String, body: Dictionary) -> _api.RivetRequest: + return _api.POST(self, path, body) + +## @experimental +func GET(path: String, body: Dictionary = {}) -> _api.RivetRequest: + return _api.GET(self, path, body) + +## @experimental +func PUT(path: String, body: Dictionary = {}) -> _api.RivetRequest: + return _api.PUT(self, path, body) diff --git a/godot/bomber/addons/rivet_api/rivet_helper.gd b/godot/bomber/addons/rivet/rivet_helper.gd similarity index 64% rename from godot/bomber/addons/rivet_api/rivet_helper.gd rename to godot/bomber/addons/rivet/rivet_helper.gd index 1a66645..c0b88b3 100644 --- a/godot/bomber/addons/rivet_api/rivet_helper.gd +++ b/godot/bomber/addons/rivet/rivet_helper.gd @@ -1,7 +1,11 @@ extends Node +## Triggered if running a dedicated server. signal start_server() +## Triggered if running a client. +signal start_client() + var multiplayer_setup = false ## All player tokens for players that have authenticated. @@ -16,11 +20,6 @@ var player_tokens = {} var player_token = null -func _ready(): - var dotenv = DotEnv.new() - dotenv.config() - - ## Determines if running as a dedicated server. func is_dedicated_server() -> bool: return OS.get_cmdline_user_args().has("--server") @@ -28,6 +27,7 @@ func is_dedicated_server() -> bool: ## Sets up the authentication hooks on SceneMultiplayer. func setup_multiplayer(): + RivetHelper._assert(!multiplayer_setup, "RivetHelper.setup_multiplayer already called") multiplayer_setup = true var scene_multiplayer = multiplayer as SceneMultiplayer @@ -43,12 +43,15 @@ func setup_multiplayer(): if is_dedicated_server(): rivet_print("Starting server") start_server.emit() + else: + rivet_print("Starting client") + start_client.emit() ## Sets the player token for the next authentication challenge. func set_player_token(_player_token: String): - assert(multiplayer_setup, "RivetHelper.setup_multiplayer has not been called") - assert(!is_dedicated_server(), "cannot called RivetHelper.set_player_token on server") + RivetHelper._assert(multiplayer_setup, "RivetHelper.setup_multiplayer has not been called") + RivetHelper._assert(!is_dedicated_server(), "cannot called RivetHelper.set_player_token on server") player_token = _player_token @@ -63,9 +66,17 @@ func _auth_callback(id: int, buf: PackedByteArray): rivet_print("Player authenticating %s: %s" % [id, data]) player_tokens[id] = data.player_token - RivetClient.player_connected({ + + var response = await Rivet.matchmaker.players.connected({ "player_token": data.player_token - }, _rivet_player_connected.bind(id), _rivet_player_connect_failed.bind(id)) + }) + + if response.result == OK: + rivet_print("Player authenticated for %s" % id) + (multiplayer as SceneMultiplayer).complete_auth(id) + else: + rivet_print("Player authentiation failed for %s: %s" % [id, response.body]) + (multiplayer as SceneMultiplayer).disconnect_peer(id) else: # Auto-approve if not a server (multiplayer as SceneMultiplayer).complete_auth(id) @@ -78,27 +89,27 @@ func _player_authenticating(id): func _player_authentication_failed(id): rivet_print("Authentication failed for %s" % id) - multiplayer.set_network_peer(null) -# connection_failed.emit() + multiplayer.set_multiplayer_peer(null) func _player_disconnected(id): if multiplayer.is_server(): var player_token = player_tokens.get(id) player_tokens.erase(id) rivet_print("Removing player %s" % player_token) - - RivetClient.player_disconnected({ - "player_token": player_token - }, func(_x): pass, func(_x): pass) - -func _rivet_player_connected(_body, id: int): - rivet_print("Player authenticated for %s" % id) - (multiplayer as SceneMultiplayer).complete_auth(id) + var response = await Rivet.matchmaker.players.disconnected({ + "player_token": player_token + }) -func _rivet_player_connect_failed(error, id: int): - rivet_print("Player authentiation failed for %s: %s" % [id, error]) - (multiplayer as SceneMultiplayer).disconnect_peer(id) func rivet_print(message: String): print("[Rivet] %s" % message) + +func _assert(condition: bool, message: String = "Assertion failed"): + if not condition: + # For now, we won't crash the game if an assertion fails. There are a + # few reasons for this. See discussion: + # https://app.graphite.dev/github/pr/rivet-gg/plugin-godot/33/Assert-fix# + # OS.crash(message) + + rivet_print(message) diff --git a/godot/bomber/addons/rivet_api/dock/dock.gd b/godot/bomber/addons/rivet_api/dock/dock.gd deleted file mode 100644 index 3f51af3..0000000 --- a/godot/bomber/addons/rivet_api/dock/dock.gd +++ /dev/null @@ -1,84 +0,0 @@ -@tool -extends VBoxContainer - -var poll_timer: Timer -var rng := RandomNumberGenerator.new() - -var server_pid = null - -func _ready(): - poll_timer = Timer.new() - poll_timer.autostart = true - poll_timer.timeout.connect(_poll_server_status) - add_child(poll_timer) - - -func exec_sh(script: String) -> int: - return OS.create_process("sh", ["-c", script]) -# OS.create_process("CMD.exe", ["/C", script], output) - - -func run_rivet_command(args: PackedStringArray): - var execute_id = rng.randi() - return OS.create_process("rivet", args) - - -## Checks if the server process is still running. -func _poll_server_status(): - # Check if server still running - if server_pid != null and !OS.is_process_running(server_pid): - print("Server stopped") - server_pid = null - - # Update stop button - $StopServer.disabled = server_pid == null - if server_pid != null: - $StartServer.text = "Restart Server" - $ServerPID.text = "Process ID: %s" % server_pid - $ServerPID.visible = true - else: - $StartServer.text = "Start Server" - $StopServer.text = "Stop Server" - $ServerPID.visible = false - -func start_server(): - if server_pid != null: - stop_server() - - server_pid = OS.create_process(OS.get_executable_path(), ["--headless", "--", "--server"]) - - _poll_server_status() - - -## Kills the server if running. -func stop_server(): - if server_pid != null: - print("Stopped serer") - OS.kill(server_pid) - server_pid = null - _poll_server_status() - else: - print("Server not running") - - -# MARK: UI -func _on_dashboard_pressed(): - run_rivet_command(["game", "dashboard"]) - - -func _on_start_server_pressed(): - start_server() - - -func _on_stop_server_pressed(): - stop_server() - - -#func _on_edit_config_pressed(): -# print(ProjectSettings.globalize_path("res://rivet.toml")) -# OS.shell_open("file://%s" % ProjectSettings.globalize_path("res://rivet.toml")) - - -#func _on_deploy_pressed(): -# deploy_pid = run_rivet_command(["deploy", "--namespace", "prod"]) -# _poll_server_status() diff --git a/godot/bomber/addons/rivet_api/dock/dock.tscn b/godot/bomber/addons/rivet_api/dock/dock.tscn deleted file mode 100644 index d148e5f..0000000 --- a/godot/bomber/addons/rivet_api/dock/dock.tscn +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5ed1dfcb391a43f8969dd47c5959316c408fa07beab4ad0235e0a544633329fc -size 2107 diff --git a/godot/bomber/addons/rivet_api/dotenv.gd b/godot/bomber/addons/rivet_api/dotenv.gd deleted file mode 100644 index 5d5cf90..0000000 --- a/godot/bomber/addons/rivet_api/dotenv.gd +++ /dev/null @@ -1,33 +0,0 @@ -extends Node -class_name DotEnv - -func config(): - var path = "res://.env" - - # Check if .env exists - if !FileAccess.file_exists(path): - RivetHelper.rivet_print(".env does not exist") - return - - # Read .env file - var file = FileAccess.open(path, FileAccess.READ) - while !file.eof_reached(): - var line = file.get_line() - - # Ignore comments - if line.begins_with("#"): - continue - - # Split line - var split = line.split("=", true, 2) - if split.size() != 2: - continue - var key = split[0] - var value = split[1] - - # Trim quotes from value - if value.begins_with("\"") and value.ends_with("\""): - value = value.substr(1, value.length() - 2) - - # Set env - OS.set_environment(split[0], split[1]) diff --git a/godot/bomber/addons/rivet_api/images/icon-circle.png b/godot/bomber/addons/rivet_api/images/icon-circle.png deleted file mode 100644 index 67da309..0000000 --- a/godot/bomber/addons/rivet_api/images/icon-circle.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f581b038ff8e7a1e81b5e20693f8ad0b9dbfde02cebe97092bb4ba6f202cf526 -size 11263 diff --git a/godot/bomber/addons/rivet_api/images/icon-circle.png.import b/godot/bomber/addons/rivet_api/images/icon-circle.png.import deleted file mode 100644 index 2ebd2b6..0000000 --- a/godot/bomber/addons/rivet_api/images/icon-circle.png.import +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9c7ccf8d99e564fe5476756802753428d79249c7829272b1e917385203f0ca2f -size 791 diff --git a/godot/bomber/addons/rivet_api/rivet_api.gd b/godot/bomber/addons/rivet_api/rivet_api.gd deleted file mode 100644 index 3a254fe..0000000 --- a/godot/bomber/addons/rivet_api/rivet_api.gd +++ /dev/null @@ -1,27 +0,0 @@ -@tool -extends EditorPlugin - -# MARK: Plugin -const AUTO_LOAD_RIVET_CLIENT = "RivetClient" -const AUTO_LOAD_RIVET_HELPER = "RivetHelper" - -var dock: Control - -func _enter_tree(): - # Add singleton - add_autoload_singleton(AUTO_LOAD_RIVET_CLIENT, "res://addons/rivet_api/rivet_client.gd") - add_autoload_singleton(AUTO_LOAD_RIVET_HELPER, "res://addons/rivet_api/rivet_helper.gd") - - # Add dock - dock = preload("res://addons/rivet_api/dock/dock.tscn").instantiate() - add_control_to_dock(DOCK_SLOT_LEFT_BR, dock) - -func _exit_tree(): - # Remove singleton - remove_autoload_singleton(AUTO_LOAD_RIVET_CLIENT) - remove_autoload_singleton(AUTO_LOAD_RIVET_HELPER) - - # Remove dock - remove_control_from_docks(dock) - dock.free() - diff --git a/godot/bomber/brickfloor.png b/godot/bomber/assets/brickfloor.png similarity index 100% rename from godot/bomber/brickfloor.png rename to godot/bomber/assets/brickfloor.png diff --git a/godot/bomber/assets/brickfloor.png.import b/godot/bomber/assets/brickfloor.png.import new file mode 100644 index 0000000..713e6fe --- /dev/null +++ b/godot/bomber/assets/brickfloor.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bdomqql6y50po" +path="res://.godot/imported/brickfloor.png-1ce413e4c011fbd447287b441234b9f9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/brickfloor.png" +dest_files=["res://.godot/imported/brickfloor.png-1ce413e4c011fbd447287b441234b9f9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/godot/bomber/charwalk.png b/godot/bomber/assets/charwalk.png similarity index 100% rename from godot/bomber/charwalk.png rename to godot/bomber/assets/charwalk.png diff --git a/godot/bomber/assets/charwalk.png.import b/godot/bomber/assets/charwalk.png.import new file mode 100644 index 0000000..fc5eba5 --- /dev/null +++ b/godot/bomber/assets/charwalk.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bsqovikudjr0q" +path="res://.godot/imported/charwalk.png-faa1106936dc3ced598a0e10a43eb5e9.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/charwalk.png" +dest_files=["res://.godot/imported/charwalk.png-faa1106936dc3ced598a0e10a43eb5e9.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/godot/bomber/explosion.png b/godot/bomber/assets/explosion.png similarity index 100% rename from godot/bomber/explosion.png rename to godot/bomber/assets/explosion.png diff --git a/godot/bomber/assets/explosion.png.import b/godot/bomber/assets/explosion.png.import new file mode 100644 index 0000000..19e58bd --- /dev/null +++ b/godot/bomber/assets/explosion.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://drfbkdqmj0gu2" +path="res://.godot/imported/explosion.png-3d70b655f829d2ec362997bd3be8e3ae.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/explosion.png" +dest_files=["res://.godot/imported/explosion.png-3d70b655f829d2ec362997bd3be8e3ae.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/godot/bomber/assets/export_presets.cfg b/godot/bomber/assets/export_presets.cfg new file mode 100644 index 0000000..21ab7b3 --- /dev/null +++ b/godot/bomber/assets/export_presets.cfg @@ -0,0 +1,43 @@ +[preset.0] + +name="Linux/X11" +platform="Linux/X11" +runnable=true +dedicated_server=true +custom_features="" +export_filter="customized" +customized_files={ +"res://": "strip" +} +include_filter="" +exclude_filter="" +export_path="" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false +script_encryption_key="" + +[preset.0.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_script=1 +binary_format/embed_pck=false +texture_format/bptc=true +texture_format/s3tc=true +texture_format/etc=false +texture_format/etc2=false +binary_format/architecture="x86_64" +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="#!/usr/bin/env bash +export DISPLAY=:0 +unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" +\"{temp_dir}/{exe_name}\" {cmd_args}" +ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash +kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\") +rm -rf \"{temp_dir}\"" diff --git a/godot/bomber/icon.png b/godot/bomber/assets/icon.png similarity index 100% rename from godot/bomber/icon.png rename to godot/bomber/assets/icon.png diff --git a/godot/bomber/assets/icon.png.import b/godot/bomber/assets/icon.png.import new file mode 100644 index 0000000..a54a8bb --- /dev/null +++ b/godot/bomber/assets/icon.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://cbby50724oj4c" +path="res://.godot/imported/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/icon.png" +dest_files=["res://.godot/imported/icon.png-b6a7fb2db36edd3d95dc42f1dc8c1c5d.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/godot/bomber/montserrat.otf b/godot/bomber/assets/montserrat.otf similarity index 100% rename from godot/bomber/montserrat.otf rename to godot/bomber/assets/montserrat.otf diff --git a/godot/bomber/assets/montserrat.otf.import b/godot/bomber/assets/montserrat.otf.import new file mode 100644 index 0000000..c9acc10 --- /dev/null +++ b/godot/bomber/assets/montserrat.otf.import @@ -0,0 +1,33 @@ +[remap] + +importer="font_data_dynamic" +type="FontFile" +uid="uid://knb8u535cfkw" +path="res://.godot/imported/montserrat.otf-344d7c85facec00a87358649b6f3ceaf.fontdata" + +[deps] + +source_file="res://assets/montserrat.otf" +dest_files=["res://.godot/imported/montserrat.otf-344d7c85facec00a87358649b6f3ceaf.fontdata"] + +[params] + +Rendering=null +antialiasing=1 +generate_mipmaps=false +multichannel_signed_distance_field=false +msdf_pixel_range=8 +msdf_size=48 +allow_system_fallback=true +force_autohinter=false +hinting=1 +subpixel_positioning=1 +oversampling=0.0 +Fallbacks=null +fallbacks=[] +Compress=null +compress=true +preload=[] +language_support={} +script_support={} +opentype_features={} diff --git a/godot/bomber/rock_bit.png b/godot/bomber/assets/rock_bit.png similarity index 100% rename from godot/bomber/rock_bit.png rename to godot/bomber/assets/rock_bit.png diff --git a/godot/bomber/assets/rock_bit.png.import b/godot/bomber/assets/rock_bit.png.import new file mode 100644 index 0000000..7932b2b --- /dev/null +++ b/godot/bomber/assets/rock_bit.png.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bh4gbjcayios1" +path="res://.godot/imported/rock_bit.png-7452bb52b797fd06f3864ee774cafdee.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://assets/rock_bit.png" +dest_files=["res://.godot/imported/rock_bit.png-7452bb52b797fd06f3864ee774cafdee.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/godot/bomber/assets/tileset.tres b/godot/bomber/assets/tileset.tres new file mode 100644 index 0000000..283f588 --- /dev/null +++ b/godot/bomber/assets/tileset.tres @@ -0,0 +1,19 @@ +[gd_resource type="TileSet" load_steps=3 format=3 uid="uid://do2l6lpuotti8"] + +[ext_resource type="Texture2D" uid="uid://bdomqql6y50po" path="res://assets/brickfloor.png" id="1"] + +[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_qhkfp"] +texture = ExtResource("1") +texture_region_size = Vector2i(48, 48) +0:0/0 = 0 +0:0/0/physics_layer_0/linear_velocity = Vector2(0, 0) +0:0/0/physics_layer_0/angular_velocity = 0.0 +0:0/0/physics_layer_0/polygon_0/points = PackedVector2Array(-24, -24, 24, -24, 24, 24, -24, 24) +1:0/0 = 0 +1:0/0/physics_layer_0/linear_velocity = Vector2(0, 0) +1:0/0/physics_layer_0/angular_velocity = 0.0 + +[resource] +tile_size = Vector2i(48, 48) +physics_layer_0/collision_layer = 1 +sources/0 = SubResource("TileSetAtlasSource_qhkfp") diff --git a/godot/bomber/bomb.tscn b/godot/bomber/bomb.tscn deleted file mode 100644 index 6af7f28..0000000 --- a/godot/bomber/bomb.tscn +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e5a7def879bd54e8f4dd7166d7ccb039680f27bc4a55bec78e1f8ee05c0d212f -size 4262 diff --git a/godot/bomber/brickfloor.png.import b/godot/bomber/brickfloor.png.import deleted file mode 100644 index 6320549..0000000 --- a/godot/bomber/brickfloor.png.import +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e8f3cb1339d3c62eaa2af5f52058f9432a3518d20e17042b1f93a705f3b2482d -size 764 diff --git a/godot/bomber/charwalk.png.import b/godot/bomber/charwalk.png.import deleted file mode 100644 index 8fdd94b..0000000 --- a/godot/bomber/charwalk.png.import +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:beccb80ec0da0c3bdf2688922914ea1c1e084d9e976adb5fece9fd948736dd52 -size 758 diff --git a/godot/bomber/example.toml b/godot/bomber/example.toml deleted file mode 100644 index 1a68f7c..0000000 --- a/godot/bomber/example.toml +++ /dev/null @@ -1,14 +0,0 @@ -[display] -title = "Bomber" -tutorial_url = "https://rivet.gg/learn/godot/tutorials/crash-course" -overview_weight = 100 - -[meta] -engine = "Godot" -engine_version = "4.0.0" -languages = ["GDScript"] -features = ["Matchmaker", "DynamicServers"] -platforms = ["Desktop"] -networking = "GodotHLMultiplayer" - - diff --git a/godot/bomber/explosion.png.import b/godot/bomber/explosion.png.import deleted file mode 100644 index ffeb98b..0000000 --- a/godot/bomber/explosion.png.import +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7d954947c661ffbf1975e1c76dab459b07abece85dac34501e1ab71a5a021faa -size 761 diff --git a/godot/bomber/export_presets.cfg b/godot/bomber/export_presets.cfg new file mode 100644 index 0000000..529ee6c --- /dev/null +++ b/godot/bomber/export_presets.cfg @@ -0,0 +1,39 @@ +[preset.0] + +name="Linux/X11" +platform="Linux/X11" +runnable=true +dedicated_server=false +custom_features="" +export_filter="all_resources" +include_filter="" +exclude_filter="" +export_path="" +encryption_include_filters="" +encryption_exclude_filters="" +encrypt_pck=false +encrypt_directory=false + +[preset.0.options] + +custom_template/debug="" +custom_template/release="" +debug/export_console_wrapper=1 +binary_format/embed_pck=false +texture_format/bptc=true +texture_format/s3tc=true +texture_format/etc=false +texture_format/etc2=false +binary_format/architecture="x86_64" +ssh_remote_deploy/enabled=false +ssh_remote_deploy/host="user@host_ip" +ssh_remote_deploy/port="22" +ssh_remote_deploy/extra_args_ssh="" +ssh_remote_deploy/extra_args_scp="" +ssh_remote_deploy/run_script="#!/usr/bin/env bash +export DISPLAY=:0 +unzip -o -q \"{temp_dir}/{archive_name}\" -d \"{temp_dir}\" +\"{temp_dir}/{exe_name}\" {cmd_args}" +ssh_remote_deploy/cleanup_script="#!/usr/bin/env bash +kill $(pgrep -x -f \"{temp_dir}/{exe_name} {cmd_args}\") +rm -rf \"{temp_dir}\"" diff --git a/godot/bomber/icon.png.import b/godot/bomber/icon.png.import deleted file mode 100644 index 3d3246a..0000000 --- a/godot/bomber/icon.png.import +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dfbfc2858181ccf39cf78242cd9982b0dd6824a496e07b05ad48b7b2691febc2 -size 746 diff --git a/godot/bomber/lobby.tscn b/godot/bomber/lobby.tscn deleted file mode 100644 index 4be2f1e..0000000 --- a/godot/bomber/lobby.tscn +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cff440ef2545139ff2152d6f5bb45706c37a6c485a0961c3843f3a87a769958f -size 2905 diff --git a/godot/bomber/montserrat.otf.import b/godot/bomber/montserrat.otf.import deleted file mode 100644 index 71e4283..0000000 --- a/godot/bomber/montserrat.otf.import +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cee51a58286bbed7068867a4928b4e707d336255ce587fcb4544da48b37bd2d1 -size 666 diff --git a/godot/bomber/player.tscn b/godot/bomber/player.tscn deleted file mode 100644 index 974190e..0000000 --- a/godot/bomber/player.tscn +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ae24881be6a8fda271a0e47875090afde79aa178d5cc2e2a0503d64b4f9b5b91 -size 5450 diff --git a/godot/bomber/project.godot b/godot/bomber/project.godot index 134fd3f..a29235b 100644 --- a/godot/bomber/project.godot +++ b/godot/bomber/project.godot @@ -14,15 +14,16 @@ config/name="Multiplayer Bomber" config/description="A multiplayer implementation of the classical bomberman game. One of the players should press 'host', while the other should type in his address and press 'play'." -run/main_scene="res://lobby.tscn" -config/features=PackedStringArray("4.0") -config/icon="res://icon.png" +run/main_scene="res://scenes/lobby.tscn" +config/features=PackedStringArray("4.2") +config/icon="res://assets/icon.png" [autoload] -gamestate="*res://gamestate.gd" -RivetClient="*res://addons/rivet_api/rivet_client.gd" -RivetHelper="*res://addons/rivet_api/rivet_helper.gd" +gamestate="*res://scripts/gamestate.gd" +RivetHelper="*res://addons/rivet/rivet_helper.gd" +Rivet="*res://addons/rivet/rivet_global.gd" +RivetGlobal="*res://addons/rivet/rivet_global.gd" [display] @@ -35,7 +36,7 @@ project/assembly_name="Multiplayer Bomber" [editor_plugins] -enabled=PackedStringArray("res://addons/rivet_api/plugin.cfg") +enabled=PackedStringArray("res://addons/rivet/plugin.cfg") [input] @@ -76,7 +77,7 @@ set_bomb={ "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":0,"echo":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null) , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":1,"pressure":0.0,"pressed":false,"script":null) -, Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"pressed":false,"double_click":false,"script":null) +, Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(0, 0),"global_position":Vector2(0, 0),"factor":1.0,"button_index":1,"canceled":false,"pressed":false,"double_click":false,"script":null) ] } diff --git a/godot/bomber/rivet.toml b/godot/bomber/rivet.toml deleted file mode 100644 index 9d8efdd..0000000 --- a/godot/bomber/rivet.toml +++ /dev/null @@ -1,52 +0,0 @@ -# === Rivet Version Configuration === -# -# - More info: https://docs.rivet.gg/general/concepts/rivet-version-config -# - Reference: https://docs.rivet.gg/cloud/api/post-games-versions#body -# - Publish a new version with `rivet publish` -# - -# How the game lobbies run and how players connect to the game. -# -# https://docs.rivet.gg/matchmaker/introduction -[matchmaker] - # How many players can join a specific lobby. - # - # Read more about matchmaking: https://docs.rivet.gg/matchmaker/concepts/finding-lobby - max_players = 12 - - # The hardware to provide for lobbies. - # - # Available tiers: https://docs.rivet.gg/serverless-lobbies/concepts/available-tiers - tier = "basic-1d1" - -# Which regions the game should be available in. -# -# Available regions: https://docs.rivet.gg/serverless-lobbies/concepts/available-regions -[matchmaker.regions] - lnd-sfo = {} - lnd-fra = {} - -# Runtime configuration for the lobby's Docker container. -[matchmaker.docker] - # If you're unfamiliar with Docker, here's how to write your own - # Dockerfile: - # https://docker-curriculum.com/#dockerfile - dockerfile = "Dockerfile" - - # Which ports to allow players to connect to. Multiple ports can be defined - # with different protocols. - # - # How ports work: https://docs.rivet.gg/serverless-lobbies/concepts/ports - ports.default = { port = 10567, protocol = "udp" } - -# What game modes are avaiable. -# -# Properties like `max_players`, `tier`, `dockerfile`, `regions`, and more can -# be overriden for specific game modes. -[matchmaker.game_modes] - default = {} - -[kv] - -[identity] - diff --git a/godot/bomber/rock.tscn b/godot/bomber/rock.tscn deleted file mode 100644 index 5b15ea0..0000000 --- a/godot/bomber/rock.tscn +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4165c9e29444ba7c820be42b6df02431b068cdef64a827497e3ad0b5a00c7739 -size 1453 diff --git a/godot/bomber/rock_bit.png.import b/godot/bomber/rock_bit.png.import deleted file mode 100644 index e33e85e..0000000 --- a/godot/bomber/rock_bit.png.import +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:779b33ee0a2930659c5cffc18bef2816e84d7de34d583067267f4ea65c725337 -size 758 diff --git a/godot/bomber/scenes/bomb.tscn b/godot/bomber/scenes/bomb.tscn new file mode 100644 index 0000000..8fa12c8 --- /dev/null +++ b/godot/bomber/scenes/bomb.tscn @@ -0,0 +1,135 @@ +[gd_scene load_steps=9 format=3 uid="uid://enwoaqi0rnei"] + +[ext_resource type="Script" path="res://scripts/bomb.gd" id="1"] +[ext_resource type="Texture2D" uid="uid://bdomqql6y50po" path="res://assets/brickfloor.png" id="2"] +[ext_resource type="Texture2D" uid="uid://drfbkdqmj0gu2" path="res://assets/explosion.png" id="3"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_1ih13"] +size = Vector2(16, 192) + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_whso6"] +size = Vector2(192, 16) + +[sub_resource type="Curve" id="Curve_4yges"] +max_value = 2.0 +_data = [Vector2(0.00150494, 0.398437), 0.0, 0.0, 0, 0, Vector2(0.0152287, 1.42969), 0.0, 0.0, 0, 0, Vector2(0.478607, 1.30078), 0.0, 0.0, 0, 0, Vector2(1, 0.291016), 0.0, 0.0, 0, 0] +point_count = 4 + +[sub_resource type="Animation" id="Animation_21j5c"] +length = 4.0 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Sprite:self_modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.4, 0.6, 0.8, 1.1, 1.3, 1.5, 1.8, 1.9, 2, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 3), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(8, 8, 8, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(8, 8, 8, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(8, 8, 8, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(8, 8, 8, 1), Color(1, 1, 1, 1), Color(8, 8, 8, 1), Color(1, 1, 1, 1), Color(8, 8, 8, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 0)] +} +tracks/1/type = "method" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(2.8, 3.4), +"transitions": PackedFloat32Array(1, 1), +"values": [{ +"args": [], +"method": &"explode" +}, { +"args": [], +"method": &"done" +}] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Explosion1:emitting") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 2.8), +"transitions": PackedFloat32Array(1, 1), +"update": 1, +"values": [false, true] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("Explosion2:emitting") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0, 2.8), +"transitions": PackedFloat32Array(1, 1), +"update": 1, +"values": [false, true] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_h2w7m"] +_data = { +"anim": SubResource("Animation_21j5c") +} + +[node name="Bomb" type="Area2D"] +monitorable = false +script = ExtResource("1") + +[node name="Sprite" type="Sprite2D" parent="."] +self_modulate = Color(1, 1, 1, 0) +position = Vector2(-2.92606, -2.92606) +texture = ExtResource("2") +region_enabled = true +region_rect = Rect2(144, 0, 48, 48) + +[node name="Shape1" type="CollisionShape2D" parent="."] +shape = SubResource("RectangleShape2D_1ih13") + +[node name="Shape2" type="CollisionShape2D" parent="."] +shape = SubResource("RectangleShape2D_whso6") + +[node name="Explosion1" type="CPUParticles2D" parent="."] +emitting = false +lifetime = 0.5 +one_shot = true +explosiveness = 0.95 +texture = ExtResource("3") +emission_shape = 3 +emission_rect_extents = Vector2(80, 1) +gravity = Vector2(0, 0) +initial_velocity_min = 1.0 +initial_velocity_max = 1.0 +angular_velocity_min = 187.35 +angular_velocity_max = 188.35 +scale_amount_curve = SubResource("Curve_4yges") + +[node name="Explosion2" type="CPUParticles2D" parent="."] +rotation = 1.57162 +scale = Vector2(1, 1) +emitting = false +lifetime = 0.5 +one_shot = true +explosiveness = 0.95 +texture = ExtResource("3") +emission_shape = 3 +emission_rect_extents = Vector2(80, 1) +gravity = Vector2(0, 0) +initial_velocity_min = 1.0 +initial_velocity_max = 1.0 +angular_velocity_min = 187.35 +angular_velocity_max = 188.35 +scale_amount_curve = SubResource("Curve_4yges") + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +autoplay = "anim" +libraries = { +"": SubResource("AnimationLibrary_h2w7m") +} + +[connection signal="body_entered" from="." to="." method="_on_bomb_body_enter"] +[connection signal="body_exited" from="." to="." method="_on_bomb_body_exit"] diff --git a/godot/bomber/scenes/lobby.tscn b/godot/bomber/scenes/lobby.tscn new file mode 100644 index 0000000..72d2198 --- /dev/null +++ b/godot/bomber/scenes/lobby.tscn @@ -0,0 +1,121 @@ +[gd_scene load_steps=2 format=3 uid="uid://jhdlqsokif5o"] + +[ext_resource type="Script" path="res://scripts/lobby.gd" id="1_q5pu8"] + +[node name="Lobby" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 2 +size_flags_vertical = 2 +script = ExtResource("1_q5pu8") + +[node name="Players" type="Panel" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -126.0 +offset_top = -177.5 +offset_right = 126.0 +offset_bottom = 177.5 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 2 +size_flags_vertical = 2 + +[node name="Label" type="Label" parent="Players"] +layout_mode = 0 +offset_left = 26.0 +offset_top = 18.0 +offset_right = 142.0 +offset_bottom = 32.0 +size_flags_horizontal = 2 +size_flags_vertical = 0 +text = "Awaiting Players..." + +[node name="Start" type="Button" parent="Players"] +layout_mode = 0 +offset_left = 68.0 +offset_top = 307.0 +offset_right = 193.0 +offset_bottom = 336.0 +size_flags_horizontal = 2 +size_flags_vertical = 2 +text = "START!" + +[node name="List" type="ItemList" parent="Players"] +layout_mode = 0 +offset_left = 25.0 +offset_top = 37.0 +offset_right = 229.0 +offset_bottom = 296.0 +size_flags_horizontal = 2 +size_flags_vertical = 2 + +[node name="Connect" type="Panel" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -138.0 +offset_top = -71.0 +offset_right = 139.0 +offset_bottom = 71.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 2 +size_flags_vertical = 2 + +[node name="NameLabel" type="Label" parent="Connect"] +layout_mode = 0 +offset_left = 54.5 +offset_top = 8.5 +offset_right = 105.5 +offset_bottom = 34.5 +size_flags_horizontal = 2 +size_flags_vertical = 0 +text = "Name:" + +[node name="Name" type="LineEdit" parent="Connect"] +layout_mode = 0 +offset_left = 54.5 +offset_top = 37.5 +offset_right = 210.5 +offset_bottom = 68.5 +size_flags_horizontal = 2 +size_flags_vertical = 2 +text = "The Warrior" + +[node name="ErrorLabel" type="Label" parent="Connect"] +layout_mode = 0 +offset_left = 15.0 +offset_top = 125.0 +offset_right = 257.0 +offset_bottom = 139.0 +size_flags_horizontal = 2 +size_flags_vertical = 0 +theme_override_colors/font_color = Color(0.820312, 0.291595, 0.291595, 1) + +[node name="Join" type="Button" parent="Connect"] +layout_mode = 0 +offset_left = 77.5 +offset_top = 84.5 +offset_right = 170.5 +offset_bottom = 115.5 +size_flags_horizontal = 2 +size_flags_vertical = 2 +text = "Find Lobby" + +[node name="ErrorDialog" type="AcceptDialog" parent="."] + +[connection signal="pressed" from="Players/Start" to="." method="_on_start_pressed"] +[connection signal="pressed" from="Connect/Join" to="." method="_on_join_pressed"] diff --git a/godot/bomber/scenes/player.tscn b/godot/bomber/scenes/player.tscn new file mode 100644 index 0000000..ac1dd88 --- /dev/null +++ b/godot/bomber/scenes/player.tscn @@ -0,0 +1,206 @@ +[gd_scene load_steps=16 format=3 uid="uid://dviwgv2ty8v6u"] + +[ext_resource type="Script" path="res://scripts/player.gd" id="1"] +[ext_resource type="Texture2D" uid="uid://bsqovikudjr0q" path="res://assets/charwalk.png" id="2"] +[ext_resource type="FontFile" uid="uid://knb8u535cfkw" path="res://assets/montserrat.otf" id="3"] +[ext_resource type="Script" path="res://scripts/player_controls.gd" id="4_k1vfr"] + +[sub_resource type="CircleShape2D" id="1"] +radius = 20.0 + +[sub_resource type="Animation" id="2"] +resource_name = "standing" +length = 0.8 +loop_mode = 1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("sprite:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.2, 0.4, 0.6), +"transitions": PackedFloat32Array(1, 1, 1, 1), +"update": 1, +"values": [0, 4, 8, 12] +} + +[sub_resource type="Animation" id="3"] +resource_name = "stunned" +length = 1.2 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("sprite:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [0] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".:stunned") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(1), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [false] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("sprite:rotation") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [-6.28319, 0.0] +} + +[sub_resource type="Animation" id="4"] +length = 0.8 +loop_mode = 1 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("sprite:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.2, 0.4, 0.6), +"transitions": PackedFloat32Array(1, 1, 1, 1), +"update": 1, +"values": [0, 4, 8, 12] +} + +[sub_resource type="Animation" id="5"] +length = 0.8 +loop_mode = 1 +step = 0.2 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("sprite:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.2, 0.4, 0.6), +"transitions": PackedFloat32Array(1, 1, 1, 1), +"update": 1, +"values": [1, 5, 9, 13] +} + +[sub_resource type="Animation" id="6"] +length = 0.8 +loop_mode = 1 +step = 0.2 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("sprite:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.2, 0.4, 0.6), +"transitions": PackedFloat32Array(1, 1, 1, 1), +"update": 1, +"values": [3, 7, 11, 15] +} + +[sub_resource type="Animation" id="7"] +length = 0.8 +loop_mode = 1 +step = 0.2 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("sprite:frame") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.2, 0.4, 0.6), +"transitions": PackedFloat32Array(1, 1, 1, 1), +"update": 1, +"values": [2, 6, 10, 14] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_yb83i"] +_data = { +"standing": SubResource("2"), +"stunned": SubResource("3"), +"walk_down": SubResource("4"), +"walk_left": SubResource("5"), +"walk_right": SubResource("6"), +"walk_up": SubResource("7") +} + +[sub_resource type="LabelSettings" id="LabelSettings_5huhx"] +outline_size = 8 +outline_color = Color(0, 0, 0, 1) + +[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_sh64w"] +properties/0/path = NodePath(".:synced_position") +properties/0/spawn = true +properties/0/sync = true +properties/1/path = NodePath("label:text") +properties/1/spawn = true +properties/1/sync = false + +[sub_resource type="SceneReplicationConfig" id="SceneReplicationConfig_w53uu"] +properties/0/path = NodePath(".:motion") +properties/0/spawn = true +properties/0/sync = true +properties/1/path = NodePath(".:bombing") +properties/1/spawn = true +properties/1/sync = true + +[node name="player" type="CharacterBody2D"] +z_index = 10 +motion_mode = 1 +script = ExtResource("1") + +[node name="sprite" type="Sprite2D" parent="."] +position = Vector2(0.0750351, 6.23615) +rotation = -6.28319 +texture = ExtResource("2") +offset = Vector2(-0.0750351, -6.23615) +hframes = 4 +vframes = 4 + +[node name="shape" type="CollisionShape2D" parent="."] +shape = SubResource("1") + +[node name="anim" type="AnimationPlayer" parent="."] +libraries = { +"": SubResource("AnimationLibrary_yb83i") +} + +[node name="label" type="Label" parent="."] +offset_left = -82.0 +offset_top = -35.0 +offset_right = 85.0 +offset_bottom = -14.0 +size_flags_horizontal = 2 +size_flags_vertical = 0 +theme_override_fonts/font = ExtResource("3") +theme_override_font_sizes/font_size = 16 +text = "Player 1" +label_settings = SubResource("LabelSettings_5huhx") +horizontal_alignment = 1 + +[node name="MultiplayerSynchronizer" type="MultiplayerSynchronizer" parent="."] +replication_config = SubResource("SceneReplicationConfig_sh64w") + +[node name="Inputs" type="Node" parent="."] +script = ExtResource("4_k1vfr") + +[node name="InputsSync" type="MultiplayerSynchronizer" parent="Inputs"] +replication_config = SubResource("SceneReplicationConfig_w53uu") diff --git a/godot/bomber/scenes/rock.tscn b/godot/bomber/scenes/rock.tscn new file mode 100644 index 0000000..3afd012 --- /dev/null +++ b/godot/bomber/scenes/rock.tscn @@ -0,0 +1,58 @@ +[gd_scene load_steps=6 format=3 uid="uid://bao3yernlglws"] + +[ext_resource type="Script" path="res://scripts/rock.gd" id="1"] +[ext_resource type="Texture2D" uid="uid://bdomqql6y50po" path="res://assets/brickfloor.png" id="2"] + +[sub_resource type="RectangleShape2D" id="1"] +size = Vector2(48, 48) + +[sub_resource type="Animation" id="2"] +resource_name = "explode" +tracks/0/type = "method" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(1), +"transitions": PackedFloat32Array(1), +"values": [{ +"args": [], +"method": &"queue_free" +}] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Sprite:visible") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [false] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_6pqaw"] +_data = { +"explode": SubResource("2") +} + +[node name="Rock" type="CharacterBody2D"] +motion_mode = 1 +script = ExtResource("1") + +[node name="Sprite" type="Sprite2D" parent="."] +texture = ExtResource("2") +region_enabled = true +region_rect = Rect2(96, 0, 48, 48) + +[node name="Shape" type="CollisionShape2D" parent="."] +shape = SubResource("1") + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +"": SubResource("AnimationLibrary_6pqaw") +} diff --git a/godot/bomber/scenes/tile_scene.tscn b/godot/bomber/scenes/tile_scene.tscn new file mode 100644 index 0000000..abcee0e --- /dev/null +++ b/godot/bomber/scenes/tile_scene.tscn @@ -0,0 +1,23 @@ +[gd_scene load_steps=3 format=3 uid="uid://c5m3rogpaglk1"] + +[ext_resource type="Texture2D" uid="uid://bdomqql6y50po" path="res://assets/brickfloor.png" id="1"] + +[sub_resource type="RectangleShape2D" id="1"] +size = Vector2(48, 48) + +[node name="TileScene" type="Node2D"] + +[node name="Wall" type="Sprite2D" parent="."] +position = Vector2(24, 24) +texture = ExtResource("1") +region_rect = Rect2(0, 0, 48, 48) + +[node name="StaticBody2D" type="StaticBody2D" parent="Wall"] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Wall/StaticBody2D"] +shape = SubResource("1") + +[node name="Floor" type="Sprite2D" parent="."] +position = Vector2(72, 24) +texture = ExtResource("1") +region_rect = Rect2(48, 0, 48, 48) diff --git a/godot/bomber/scenes/world.tscn b/godot/bomber/scenes/world.tscn new file mode 100644 index 0000000..80aea31 --- /dev/null +++ b/godot/bomber/scenes/world.tscn @@ -0,0 +1,310 @@ +[gd_scene load_steps=6 format=3 uid="uid://by3f5o7dyoqx4"] + +[ext_resource type="TileSet" uid="uid://do2l6lpuotti8" path="res://assets/tileset.tres" id="1"] +[ext_resource type="PackedScene" uid="uid://bao3yernlglws" path="res://scenes/rock.tscn" id="2"] +[ext_resource type="Script" path="res://scripts/score.gd" id="3"] +[ext_resource type="FontFile" uid="uid://knb8u535cfkw" path="res://assets/montserrat.otf" id="4"] +[ext_resource type="Script" path="res://scripts/bomb_spawner.gd" id="6_ac5ja"] + +[node name="World" type="Node2D"] + +[node name="TileMap" type="TileMap" parent="."] +tile_set = ExtResource("1") +rendering_quadrant_size = 48 +format = 2 +layer_0/tile_data = PackedInt32Array(0, 0, 0, 65536, 0, 0, 131072, 0, 0, 196608, 0, 0, 262144, 0, 0, 327680, 0, 0, 393216, 0, 0, 458752, 0, 0, 524288, 0, 0, 589824, 0, 0, 655360, 0, 0, 720896, 0, 0, 786432, 0, 0, 1, 0, 0, 65537, 65536, 0, 131073, 65536, 0, 196609, 65536, 0, 262145, 65536, 0, 327681, 65536, 0, 393217, 65536, 0, 458753, 65536, 0, 524289, 65536, 0, 589825, 65536, 0, 655361, 65536, 0, 720897, 65536, 0, 786433, 0, 0, 2, 0, 0, 65538, 65536, 0, 131074, 0, 0, 196610, 65536, 0, 262146, 0, 0, 327682, 65536, 0, 393218, 0, 0, 458754, 65536, 0, 524290, 0, 0, 589826, 65536, 0, 655362, 0, 0, 720898, 65536, 0, 786434, 0, 0, 3, 0, 0, 65539, 65536, 0, 131075, 65536, 0, 196611, 65536, 0, 262147, 65536, 0, 327683, 65536, 0, 393219, 65536, 0, 458755, 65536, 0, 524291, 0, 0, 589827, 65536, 0, 655363, 65536, 0, 720899, 65536, 0, 786435, 0, 0, 4, 0, 0, 65540, 65536, 0, 131076, 0, 0, 196612, 0, 0, 262148, 0, 0, 327684, 65536, 0, 393220, 0, 0, 458756, 65536, 0, 524292, 0, 0, 589828, 65536, 0, 655364, 0, 0, 720900, 65536, 0, 786436, 0, 0, 5, 0, 0, 65541, 65536, 0, 131077, 65536, 0, 196613, 65536, 0, 262149, 65536, 0, 327685, 65536, 0, 393221, 65536, 0, 458757, 65536, 0, 524293, 65536, 0, 589829, 65536, 0, 655365, 65536, 0, 720901, 65536, 0, 786437, 0, 0, 6, 0, 0, 65542, 65536, 0, 131078, 0, 0, 196614, 65536, 0, 262150, 0, 0, 327686, 0, 0, 393222, 0, 0, 458758, 65536, 0, 524294, 0, 0, 589830, 65536, 0, 655366, 0, 0, 720902, 65536, 0, 786438, 0, 0, 7, 0, 0, 65543, 65536, 0, 131079, 65536, 0, 196615, 65536, 0, 262151, 65536, 0, 327687, 65536, 0, 393223, 65536, 0, 458759, 65536, 0, 524295, 65536, 0, 589831, 65536, 0, 655367, 65536, 0, 720903, 65536, 0, 786439, 0, 0, 8, 0, 0, 65544, 65536, 0, 131080, 0, 0, 196616, 65536, 0, 262152, 0, 0, 327688, 65536, 0, 393224, 0, 0, 458760, 65536, 0, 524296, 0, 0, 589832, 65536, 0, 655368, 0, 0, 720904, 65536, 0, 786440, 0, 0, 9, 0, 0, 65545, 65536, 0, 131081, 65536, 0, 196617, 65536, 0, 262153, 65536, 0, 327689, 65536, 0, 393225, 65536, 0, 458761, 65536, 0, 524297, 65536, 0, 589833, 65536, 0, 655369, 65536, 0, 720905, 65536, 0, 786441, 0, 0, 10, 0, 0, 65546, 65536, 0, 131082, 0, 0, 196618, 0, 0, 262154, 0, 0, 327690, 65536, 0, 393226, 0, 0, 458762, 65536, 0, 524298, 0, 0, 589834, 65536, 0, 655370, 0, 0, 720906, 65536, 0, 786442, 0, 0, 11, 0, 0, 65547, 65536, 0, 131083, 0, 0, 196619, 65536, 0, 262155, 65536, 0, 327691, 65536, 0, 393227, 65536, 0, 458763, 65536, 0, 524299, 65536, 0, 589835, 65536, 0, 655371, 65536, 0, 720907, 65536, 0, 786443, 0, 0, 12, 0, 0, 65548, 65536, 0, 131084, 0, 0, 196620, 65536, 0, 262156, 0, 0, 327692, 65536, 0, 393228, 0, 0, 458764, 65536, 0, 524300, 0, 0, 589836, 65536, 0, 655372, 0, 0, 720908, 65536, 0, 786444, 0, 0, 13, 0, 0, 65549, 65536, 0, 131085, 0, 0, 196621, 65536, 0, 262157, 65536, 0, 327693, 65536, 0, 393229, 0, 0, 458765, 65536, 0, 524301, 0, 0, 589837, 65536, 0, 655373, 65536, 0, 720909, 65536, 0, 786445, 0, 0, 14, 0, 0, 65550, 65536, 0, 131086, 0, 0, 196622, 65536, 0, 262158, 0, 0, 327694, 65536, 0, 393230, 0, 0, 458766, 65536, 0, 524302, 0, 0, 589838, 65536, 0, 655374, 0, 0, 720910, 65536, 0, 786446, 0, 0, 15, 0, 0, 65551, 65536, 0, 131087, 65536, 0, 196623, 65536, 0, 262159, 65536, 0, 327695, 65536, 0, 393231, 0, 0, 458767, 65536, 0, 524303, 65536, 0, 589839, 65536, 0, 655375, 65536, 0, 720911, 65536, 0, 786447, 0, 0, 16, 0, 0, 65552, 65536, 0, 131088, 0, 0, 196624, 65536, 0, 262160, 0, 0, 327696, 65536, 0, 393232, 0, 0, 458768, 65536, 0, 524304, 0, 0, 589840, 65536, 0, 655376, 0, 0, 720912, 65536, 0, 786448, 0, 0, 17, 0, 0, 65553, 65536, 0, 131089, 65536, 0, 196625, 65536, 0, 262161, 65536, 0, 327697, 65536, 0, 393233, 65536, 0, 458769, 65536, 0, 524305, 65536, 0, 589841, 65536, 0, 655377, 65536, 0, 720913, 65536, 0, 786449, 0, 0, 18, 0, 0, 65554, 65536, 0, 131090, 0, 0, 196626, 65536, 0, 262162, 0, 0, 327698, 0, 0, 393234, 0, 0, 458770, 65536, 0, 524306, 0, 0, 589842, 65536, 0, 655378, 0, 0, 720914, 65536, 0, 786450, 0, 0, 19, 0, 0, 65555, 65536, 0, 131091, 65536, 0, 196627, 65536, 0, 262163, 65536, 0, 327699, 65536, 0, 393235, 65536, 0, 458771, 65536, 0, 524307, 65536, 0, 589843, 65536, 0, 655379, 65536, 0, 720915, 65536, 0, 786451, 0, 0, 20, 0, 0, 65556, 0, 0, 131092, 0, 0, 196628, 0, 0, 262164, 0, 0, 327700, 0, 0, 393236, 0, 0, 458772, 0, 0, 524308, 0, 0, 589844, 0, 0, 655380, 0, 0, 720916, 0, 0, 786452, 0, 0, 21, 0, 0, 65557, 0, 0, 131093, 0, 0, 196629, 0, 0, 262165, 0, 0, 327701, 0, 0, 393237, 0, 0, 458773, 0, 0, 524309, 0, 0, 589845, 0, 0, 655381, 0, 0, 720917, 0, 0, 786453, 0, 0) + +[node name="SpawnPoints" type="Node2D" parent="."] + +[node name="0" type="Marker2D" parent="SpawnPoints"] +position = Vector2(72, 72) + +[node name="1" type="Marker2D" parent="SpawnPoints"] +position = Vector2(264, 216) + +[node name="2" type="Marker2D" parent="SpawnPoints"] +position = Vector2(72, 456) + +[node name="3" type="Marker2D" parent="SpawnPoints"] +position = Vector2(360, 552) + +[node name="4" type="Marker2D" parent="SpawnPoints"] +position = Vector2(840, 360) + +[node name="5" type="Marker2D" parent="SpawnPoints"] +position = Vector2(456, 264) + +[node name="6" type="Marker2D" parent="SpawnPoints"] +position = Vector2(696, 264) + +[node name="7" type="Marker2D" parent="SpawnPoints"] +position = Vector2(744, 456) + +[node name="8" type="Marker2D" parent="SpawnPoints"] +position = Vector2(312, 456) + +[node name="9" type="Marker2D" parent="SpawnPoints"] +position = Vector2(696, 72) + +[node name="10" type="Marker2D" parent="SpawnPoints"] +position = Vector2(504, 72) + +[node name="11" type="Marker2D" parent="SpawnPoints"] +position = Vector2(936, 72) + +[node name="Rocks" type="Node2D" parent="."] + +[node name="Rock0" parent="Rocks" instance=ExtResource("2")] +position = Vector2(120, 72) + +[node name="Rock1" parent="Rocks" instance=ExtResource("2")] +position = Vector2(264, 168) + +[node name="Rock2" parent="Rocks" instance=ExtResource("2")] +position = Vector2(264, 120) + +[node name="Rock3" parent="Rocks" instance=ExtResource("2")] +position = Vector2(216, 72) + +[node name="Rock4" parent="Rocks" instance=ExtResource("2")] +position = Vector2(264, 72) + +[node name="Rock5" parent="Rocks" instance=ExtResource("2")] +position = Vector2(312, 72) + +[node name="Rock6" parent="Rocks" instance=ExtResource("2")] +position = Vector2(552, 168) + +[node name="Rock7" parent="Rocks" instance=ExtResource("2")] +position = Vector2(600, 168) + +[node name="Rock8" parent="Rocks" instance=ExtResource("2")] +position = Vector2(552, 216) + +[node name="Rock9" parent="Rocks" instance=ExtResource("2")] +position = Vector2(264, 312) + +[node name="Rock10" parent="Rocks" instance=ExtResource("2")] +position = Vector2(120, 360) + +[node name="Rock11" parent="Rocks" instance=ExtResource("2")] +position = Vector2(168, 360) + +[node name="Rock12" parent="Rocks" instance=ExtResource("2")] +position = Vector2(216, 360) + +[node name="Rock13" parent="Rocks" instance=ExtResource("2")] +position = Vector2(120, 264) + +[node name="Rock14" parent="Rocks" instance=ExtResource("2")] +position = Vector2(168, 216) + +[node name="Rock15" parent="Rocks" instance=ExtResource("2")] +position = Vector2(72, 360) + +[node name="Rock16" parent="Rocks" instance=ExtResource("2")] +position = Vector2(72, 312) + +[node name="Rock17" parent="Rocks" instance=ExtResource("2")] +position = Vector2(72, 264) + +[node name="Rock18" parent="Rocks" instance=ExtResource("2")] +position = Vector2(360, 360) + +[node name="Rock19" parent="Rocks" instance=ExtResource("2")] +position = Vector2(408, 360) + +[node name="Rock20" parent="Rocks" instance=ExtResource("2")] +position = Vector2(504, 360) + +[node name="Rock21" parent="Rocks" instance=ExtResource("2")] +position = Vector2(600, 360) + +[node name="Rock22" parent="Rocks" instance=ExtResource("2")] +position = Vector2(648, 360) + +[node name="Rock23" parent="Rocks" instance=ExtResource("2")] +position = Vector2(504, 456) + +[node name="Rock24" parent="Rocks" instance=ExtResource("2")] +position = Vector2(552, 456) + +[node name="Rock25" parent="Rocks" instance=ExtResource("2")] +position = Vector2(552, 408) + +[node name="Rock26" parent="Rocks" instance=ExtResource("2")] +position = Vector2(360, 456) + +[node name="Rock27" parent="Rocks" instance=ExtResource("2")] +position = Vector2(360, 504) + +[node name="Rock28" parent="Rocks" instance=ExtResource("2")] +position = Vector2(264, 504) + +[node name="Rock29" parent="Rocks" instance=ExtResource("2")] +position = Vector2(264, 552) + +[node name="Rock30" parent="Rocks" instance=ExtResource("2")] +position = Vector2(168, 456) + +[node name="Rock31" parent="Rocks" instance=ExtResource("2")] +position = Vector2(168, 504) + +[node name="Rock32" parent="Rocks" instance=ExtResource("2")] +position = Vector2(72, 552) + +[node name="Rock33" parent="Rocks" instance=ExtResource("2")] +position = Vector2(120, 552) + +[node name="Rock34" parent="Rocks" instance=ExtResource("2")] +position = Vector2(504, 552) + +[node name="Rock35" parent="Rocks" instance=ExtResource("2")] +position = Vector2(600, 552) + +[node name="Rock36" parent="Rocks" instance=ExtResource("2")] +position = Vector2(648, 552) + +[node name="Rock37" parent="Rocks" instance=ExtResource("2")] +position = Vector2(648, 504) + +[node name="Rock38" parent="Rocks" instance=ExtResource("2")] +position = Vector2(456, 216) + +[node name="Rock39" parent="Rocks" instance=ExtResource("2")] +position = Vector2(360, 216) + +[node name="Rock40" parent="Rocks" instance=ExtResource("2")] +position = Vector2(360, 168) + +[node name="Rock41" parent="Rocks" instance=ExtResource("2")] +position = Vector2(456, 120) + +[node name="Rock42" parent="Rocks" instance=ExtResource("2")] +position = Vector2(456, 408) + +[node name="Rock43" parent="Rocks" instance=ExtResource("2")] +position = Vector2(456, 456) + +[node name="Rock44" parent="Rocks" instance=ExtResource("2")] +position = Vector2(456, 504) + +[node name="Rock45" parent="Rocks" instance=ExtResource("2")] +position = Vector2(600, 264) + +[node name="Rock46" parent="Rocks" instance=ExtResource("2")] +position = Vector2(600, 72) + +[node name="Rock47" parent="Rocks" instance=ExtResource("2")] +position = Vector2(408, 72) + +[node name="Rock48" parent="Rocks" instance=ExtResource("2")] +position = Vector2(792, 168) + +[node name="Rock49" parent="Rocks" instance=ExtResource("2")] +position = Vector2(744, 168) + +[node name="Rock50" parent="Rocks" instance=ExtResource("2")] +position = Vector2(744, 264) + +[node name="Rock51" parent="Rocks" instance=ExtResource("2")] +position = Vector2(792, 264) + +[node name="Rock52" parent="Rocks" instance=ExtResource("2")] +position = Vector2(744, 360) + +[node name="Rock53" parent="Rocks" instance=ExtResource("2")] +position = Vector2(744, 408) + +[node name="Rock54" parent="Rocks" instance=ExtResource("2")] +position = Vector2(792, 552) + +[node name="Rock55" parent="Rocks" instance=ExtResource("2")] +position = Vector2(840, 552) + +[node name="Rock56" parent="Rocks" instance=ExtResource("2")] +position = Vector2(840, 504) + +[node name="Rock57" parent="Rocks" instance=ExtResource("2")] +position = Vector2(840, 312) + +[node name="Rock58" parent="Rocks" instance=ExtResource("2")] +position = Vector2(840, 264) + +[node name="Rock59" parent="Rocks" instance=ExtResource("2")] +position = Vector2(840, 216) + +[node name="Rock60" parent="Rocks" instance=ExtResource("2")] +position = Vector2(840, 120) + +[node name="Rock61" parent="Rocks" instance=ExtResource("2")] +position = Vector2(792, 72) + +[node name="Rock62" parent="Rocks" instance=ExtResource("2")] +position = Vector2(840, 72) + +[node name="Rock63" parent="Rocks" instance=ExtResource("2")] +position = Vector2(936, 216) + +[node name="Rock64" parent="Rocks" instance=ExtResource("2")] +position = Vector2(936, 264) + +[node name="Rock65" parent="Rocks" instance=ExtResource("2")] +position = Vector2(936, 408) + +[node name="Rock66" parent="Rocks" instance=ExtResource("2")] +position = Vector2(888, 456) + +[node name="Rock67" parent="Rocks" instance=ExtResource("2")] +position = Vector2(936, 456) + +[node name="Rock68" parent="Rocks" instance=ExtResource("2")] +position = Vector2(792, 456) + +[node name="Rock69" parent="Rocks" instance=ExtResource("2")] +position = Vector2(840, 456) + +[node name="Players" type="Node2D" parent="."] + +[node name="Score" type="HBoxContainer" parent="."] +offset_right = 1024.0 +offset_bottom = 40.0 +size_flags_horizontal = 2 +size_flags_vertical = 2 +script = ExtResource("3") + +[node name="Winner" type="Label" parent="."] +visible = false +offset_right = 1031.0 +offset_bottom = 617.0 +size_flags_horizontal = 2 +size_flags_vertical = 0 +theme_override_constants/shadow_offset_x = 2 +theme_override_constants/shadow_offset_y = 2 +theme_override_fonts/font = ExtResource("4") +text = "THE WINNER IS: +YOU" + +[node name="ExitGame" type="Button" parent="Winner"] +layout_mode = 0 +offset_left = 384.0 +offset_top = 408.0 +offset_right = 649.0 +offset_bottom = 469.0 +size_flags_horizontal = 2 +size_flags_vertical = 2 +theme_override_fonts/font = ExtResource("4") +text = "EXIT GAME" + +[node name="Camera2D" type="Camera2D" parent="."] +offset = Vector2(512, 300) + +[node name="PlayerSpawner" type="MultiplayerSpawner" parent="."] +_spawnable_scenes = PackedStringArray("res://scenes/player.tscn") +spawn_path = NodePath("../Players") + +[node name="BombSpawner" type="MultiplayerSpawner" parent="."] +spawn_path = NodePath("..") +script = ExtResource("6_ac5ja") + +[connection signal="pressed" from="Winner/ExitGame" to="Score" method="_on_exit_game_pressed"] diff --git a/godot/bomber/screenshots/.gdignore b/godot/bomber/screenshots/.gdignore new file mode 100644 index 0000000..e69de29 diff --git a/godot/bomber/screenshots/bomber.png b/godot/bomber/screenshots/bomber.png new file mode 100644 index 0000000..44dd08b --- /dev/null +++ b/godot/bomber/screenshots/bomber.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:23b59808b1faed44ffdea7169f128141799aae0db2e5bee7b9cff32a3b7c2dee +size 55531 diff --git a/godot/bomber/bomb.gd b/godot/bomber/scripts/bomb.gd similarity index 100% rename from godot/bomber/bomb.gd rename to godot/bomber/scripts/bomb.gd diff --git a/godot/bomber/bomb_spawner.gd b/godot/bomber/scripts/bomb_spawner.gd similarity index 81% rename from godot/bomber/bomb_spawner.gd rename to godot/bomber/scripts/bomb_spawner.gd index 6200640..453884f 100644 --- a/godot/bomber/bomb_spawner.gd +++ b/godot/bomber/scripts/bomb_spawner.gd @@ -7,7 +7,7 @@ func _init(): func _spawn_bomb(data): if data.size() != 2 or typeof(data[0]) != TYPE_VECTOR2 or typeof(data[1]) != TYPE_INT: return null - var bomb = preload("res://bomb.tscn").instantiate() + var bomb = preload("res://scenes/bomb.tscn").instantiate() bomb.position = data[0] bomb.from_player = data[1] return bomb diff --git a/godot/bomber/gamestate.gd b/godot/bomber/scripts/gamestate.gd similarity index 85% rename from godot/bomber/gamestate.gd rename to godot/bomber/scripts/gamestate.gd index 294f614..d78ee90 100644 --- a/godot/bomber/gamestate.gd +++ b/godot/bomber/scripts/gamestate.gd @@ -26,9 +26,6 @@ signal game_error(what) func _ready(): - RivetHelper.start_server.connect(start_server) - RivetHelper.setup_multiplayer() - multiplayer.peer_connected.connect(self._player_connected) multiplayer.peer_disconnected.connect(self._player_disconnected) multiplayer.connected_to_server.connect(self._connected_ok) @@ -38,13 +35,11 @@ func _ready(): func start_server(): print("Starting server on %s" % DEFAULT_PORT) - peer = ENetMultiplayerPeer.new() peer.create_server(DEFAULT_PORT, MAX_PEERS) + multiplayer.set_multiplayer_peer(peer) - - RivetClient.lobby_ready({}, func(_x): pass, func(_x): pass) # Callback from SceneTree. @@ -84,7 +79,7 @@ func _server_disconnected(): # Callback from SceneTree, only for clients (not server). func _connected_fail(): - multiplayer.set_network_peer(null) # Remove peer + multiplayer.set_multiplayer_peer(null) # Remove peer connection_failed.emit() @@ -104,7 +99,7 @@ func unregister_player(id): @rpc("call_local", "reliable") func load_world(): # Change scene. - var world = load("res://world.tscn").instantiate() + var world = load("res://scenes/world.tscn").instantiate() get_tree().get_root().add_child(world) get_tree().get_root().get_node("Lobby").hide() @@ -116,28 +111,14 @@ func load_world(): func join_game(new_player_name): + print("Joining game as %s" % new_player_name) player_name = new_player_name - - RivetClient.find_lobby({ - "game_modes": ["default"] - }, _lobby_found, _lobby_find_failed) + peer.create_client("127.0.0.1", DEFAULT_PORT) -func _lobby_found(response): - RivetHelper.set_player_token(response.player.token) - - var port = response.ports.default - print("Connecting to ", port.host) - - peer = ENetMultiplayerPeer.new() - peer.create_client(port.hostname, port.port) multiplayer.set_multiplayer_peer(peer) -func _lobby_find_failed(error): - game_error.emit(error) - - func get_player_list(): return players.values() @@ -151,11 +132,11 @@ func get_player_name(): func begin_game(): if !multiplayer.is_server(): return - + load_world.rpc() var world = get_tree().get_root().get_node("World") - var player_scene = load("res://player.tscn") + var player_scene = load("res://scenes/player.tscn") # Create a dictionary with peer id and respective spawn points, could be improved by randomizing. var spawn_points = {} diff --git a/godot/bomber/lobby.gd b/godot/bomber/scripts/lobby.gd similarity index 100% rename from godot/bomber/lobby.gd rename to godot/bomber/scripts/lobby.gd diff --git a/godot/bomber/player.gd b/godot/bomber/scripts/player.gd similarity index 100% rename from godot/bomber/player.gd rename to godot/bomber/scripts/player.gd diff --git a/godot/bomber/player_controls.gd b/godot/bomber/scripts/player_controls.gd similarity index 100% rename from godot/bomber/player_controls.gd rename to godot/bomber/scripts/player_controls.gd diff --git a/godot/bomber/rock.gd b/godot/bomber/scripts/rock.gd similarity index 100% rename from godot/bomber/rock.gd rename to godot/bomber/scripts/rock.gd diff --git a/godot/bomber/scripts/run.sh b/godot/bomber/scripts/run.sh deleted file mode 100755 index 5e82388..0000000 --- a/godot/bomber/scripts/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -/Applications/Godot.app/Contents/MacOS/Godot --headless -- --server - diff --git a/godot/bomber/score.gd b/godot/bomber/scripts/score.gd similarity index 92% rename from godot/bomber/score.gd rename to godot/bomber/scripts/score.gd index 1358427..b7ee8e8 100644 --- a/godot/bomber/score.gd +++ b/godot/bomber/scripts/score.gd @@ -17,7 +17,6 @@ func _process(_delta): func increase_score(for_who): - assert(for_who in player_labels) var pl = player_labels[for_who] pl.score += 1 pl.label.set_text(pl.name + "\n" + str(pl.score)) @@ -28,7 +27,7 @@ func add_player(id, new_player_name): l.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER l.set_text(new_player_name + "\n" + "0") l.set_h_size_flags(SIZE_EXPAND_FILL) - var font = preload("res://montserrat.otf") + var font = preload("res://assets/montserrat.otf") l.set("custom_fonts/font", font) l.set("custom_font_size/font_size", 18) add_child(l) diff --git a/godot/bomber/tile_scene.tscn b/godot/bomber/tile_scene.tscn deleted file mode 100644 index 2ec2a1f..0000000 --- a/godot/bomber/tile_scene.tscn +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:14edd4a2a0a0664ad6ec2bec8ab6b88036ba76d88524f163de84b037b564282a -size 704 diff --git a/godot/bomber/tileset.tres b/godot/bomber/tileset.tres deleted file mode 100644 index 5cf3fb2..0000000 --- a/godot/bomber/tileset.tres +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bd5efed4ab4359ab2dd1a04ab853bdb84f97b9260722f31dcacae857aa893259 -size 752 diff --git a/godot/bomber/world.tscn b/godot/bomber/world.tscn deleted file mode 100644 index 57f3b53..0000000 --- a/godot/bomber/world.tscn +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cc2b7620bc2f00cae8db645cd1bb51eab1add7313dc497b9d082415a8d3c9dbc -size 13915