diff --git a/Actors/Objects/ElectricSocket/ElectricSocket.tscn b/Actors/Objects/ElectricSocket/ElectricSocket.tscn index 641fba7..76c865c 100644 --- a/Actors/Objects/ElectricSocket/ElectricSocket.tscn +++ b/Actors/Objects/ElectricSocket/ElectricSocket.tscn @@ -14,13 +14,13 @@ void fragment() { if (col.r/col.g > 2.) { if (length(cable_color) == 0.) { if (UV.y > 0.6 && UV.y < 0.85) { - if (int(UV.x * 200.) % 12 > 4) { + if (float(int(UV.x * 200.) % 12) > 4.) { col.rgb = vec3(0.94, 0.08, 0.08) * length(col.rgb); } else { col.rgb = vec3(0.04, 0.58, 0.98) * length(col.rgb); } } else { - if (int(UV.y * 200.) % 12 > 4) { + if (float(int(UV.y * 200.) % 12) > 4.) { col.rgb = vec3(0.94, 0.08, 0.08) * length(col.rgb); } else { col.rgb = vec3(0.04, 0.58, 0.98) * length(col.rgb); @@ -32,7 +32,6 @@ void fragment() { } COLOR = col; }" -custom_defines = "" [sub_resource type="ShaderMaterial" id=2] shader = SubResource( 1 ) diff --git a/Classes/ResourceQueue.gd b/Classes/ResourceQueue.gd index 86652cc..b88c58c 100644 --- a/Classes/ResourceQueue.gd +++ b/Classes/ResourceQueue.gd @@ -4,6 +4,7 @@ var thread var mutex var sem +var gotm_mode = false var time_max = 100 # Milliseconds. var queue = [] @@ -58,6 +59,8 @@ func cancel_resource(path): func get_progress(path): + if gotm_mode: + return 1.0 _lock("get_progress") var ret = -1 if path in pending: @@ -70,6 +73,8 @@ func get_progress(path): func is_ready(path): + if gotm_mode: + return true var ret _lock("is_ready") if path in pending: @@ -81,6 +86,8 @@ func is_ready(path): func _wait_for_resource(res, path): + if gotm_mode: + return get_resource(path) _unlock("wait_for_resource") while true: VisualServer.sync() @@ -92,6 +99,8 @@ func _wait_for_resource(res, path): func get_resource(path): + if gotm_mode: + return load(path) _lock("get_resource") if path in pending: if pending[path] is ResourceInteractiveLoader: @@ -140,8 +149,10 @@ func thread_func(_u): thread_process() -func start(): - mutex = Mutex.new() - sem = Semaphore.new() - thread = Thread.new() - thread.start(self, "thread_func", 0) +func start(gotm: bool): + gotm_mode = gotm + if not gotm: + mutex = Mutex.new() + sem = Semaphore.new() + thread = Thread.new() + thread.start(self, "thread_func", 0) diff --git a/Scenes/Global/Multiplayer.gd b/Scenes/Global/Multiplayer.gd index 14408df..117da2a 100644 --- a/Scenes/Global/Multiplayer.gd +++ b/Scenes/Global/Multiplayer.gd @@ -15,12 +15,14 @@ const SYSTEMS_UPDATE_INTERVAL = 10 const MASTER_SERVER_ADDR = "fgms.zyg.ovh" const MASTER_SERVER_UDP_PORT = 9434 const MS_GAME_CODE = "odyssey-0-a1" +const GOTM_OVERRIDE = false # Master server entry var ms_active = false var ms_key = "" var server_name = "" +onready var gotm_mode = Gotm.is_live() or GOTM_OVERRIDE var hosting = false export var player_name = "" @@ -32,6 +34,8 @@ onready var scene_manager = $"/root/SceneManager" func _ready(): player_name = "tider-" + str(randi() % 1000) + if gotm_mode: + Gotm.connect("lobby_changed", self, "_lobby_changed") func bind_events(): get_tree().connect("network_peer_connected", self, "_player_connected") @@ -71,13 +75,23 @@ func host(): # Wait just a sec to draw yield(get_tree().create_timer(0.3), "timeout") - print("Running UPNP magicks") - if discover_upnp() == UPNP.UPNP_RESULT_SUCCESS: - print("UPNP mapping added") - else: - push_warning("UPNP magicks fail, punching NAT in the face") - yield(punch_nat(), "completed") + # Run port forwarding/nat punchthrough if the platform doesn't do it already + if not gotm_mode: + print("Running UPNP magicks") + if discover_upnp() == UPNP.UPNP_RESULT_SUCCESS: + print("UPNP mapping added") + else: + push_warning("UPNP magicks fail, punching NAT in the face") + yield(punch_nat(), "completed") + server_name = player_name + "'s server" + player_info[1] = { "name": player_name } + round_info = { "map": "odyssey" } + + if gotm_mode: + # Add to master server before hosting (in GOTM mode) + create_ms_entry() + bind_events() var peer = NetworkedMultiplayerENet.new() var server_res = peer.create_server(port, MAX_PLAYERS) @@ -91,9 +105,6 @@ func host(): get_tree().network_peer = peer print("Hosting") hosting = true - server_name = player_name + "'s server" - player_info[1] = { "name": player_name } - round_info = { "map": "odyssey" } scene_manager.loading_text = null @@ -101,18 +112,27 @@ func host(): "res://Scenes/Maps/odyssey.tscn" ]) - # Add to master server - create_ms_entry() + if not gotm_mode: + # Add to master server after hosting + create_ms_entry() -func join(addr: String): +func join(server): scene_manager.enter_loader() - scene_manager.loading_text = "Joining server " + str(addr) + scene_manager.loading_text = "Joining server" # Wait just a sec to draw yield(get_tree().create_timer(0.3), "timeout") bind_events() var peer = NetworkedMultiplayerENet.new() + + var addr = null + if gotm_mode: + var success = yield(server.join(), "completed") + addr = Gotm.lobby.host.address + else: + addr = server.addr + peer.create_client(addr, SERVER_PORT) get_tree().network_peer = peer print("Connecting to ", addr) @@ -172,15 +192,20 @@ func _ms_request(endpoint: String, data): push_error("An error occurred in the HTTP request.") func ms_get_entries(): - var http_request = HTTPRequest.new() - add_child(http_request) - http_request.connect("request_completed", self, "_ms_response", ["list_games"]) - var error = http_request.request( - "https://" + MASTER_SERVER_ADDR + "/" + MS_GAME_CODE, - ["Content-Type: application/json"], - true, HTTPClient.METHOD_GET) - if error != OK: - push_error("An error occurred in the HTTP request.") + if gotm_mode: + var fetch = GotmLobbyFetch.new() + var lobbies = yield(fetch.first(), "completed") + emit_signal("ms_updated", "list_games", lobbies) + else: + var http_request = HTTPRequest.new() + add_child(http_request) + http_request.connect("request_completed", self, "_ms_response", ["list_games"]) + var error = http_request.request( + "https://" + MASTER_SERVER_ADDR + "/" + MS_GAME_CODE, + ["Content-Type: application/json"], + true, HTTPClient.METHOD_GET) + if error != OK: + push_error("An error occurred in the HTTP request.") func get_game_data(): return { @@ -191,18 +216,28 @@ func get_game_data(): } func create_ms_entry(): - _ms_request("new", { - "game_id": MS_GAME_CODE, - "data": get_game_data() - }) + if gotm_mode: + Gotm.host_lobby(true) + Gotm.lobby.hidden = false + else: + _ms_request("new", { + "game_id": MS_GAME_CODE, + "data": get_game_data() + }) update_ms_entry() func update_ms_entry(): - if ms_active: - _ms_request("update", { - "key": ms_key, - "data": get_game_data() - }) + if gotm_mode: + var data = get_game_data() + Gotm.lobby.name = data.name + for key in data: + Gotm.lobby.set_property(key, data[key]) + else: + if ms_active: + _ms_request("update", { + "key": ms_key, + "data": get_game_data() + }) var time_left = 30 func _process(delta): @@ -240,3 +275,6 @@ func get_current_map(): return GameWorld.Map.RUNTIME _: return GameWorld.Map.EMPTY + +func _lobby_changed(): + print("Lobby changed ", Gotm.lobby) diff --git a/Scenes/Global/SceneManager.gd b/Scenes/Global/SceneManager.gd index 26c6230..916c4a0 100644 --- a/Scenes/Global/SceneManager.gd +++ b/Scenes/Global/SceneManager.gd @@ -6,27 +6,33 @@ var loading_text = null var loader = preload("res://Scenes/Loader.tscn") +onready var netgame = $"/root/Multiplayer" + func _ready() -> void: - queue.start() + queue.start(netgame.gotm_mode) func enter_loader() -> void: get_tree().change_scene_to(loader) func load_scene(scene_path: String, dependencies: Array) -> void: - target_scene = scene_path - queue.queue_resource(scene_path) - for dep in dependencies: - queue.queue_resource(dep) + if not netgame.gotm_mode: + target_scene = scene_path + queue.queue_resource(scene_path) + for dep in dependencies: + queue.queue_resource(dep) + else: + get_tree().change_scene(scene_path) func _physics_process(_delta: float) -> void: - if target_scene != null: - var remaining = queue.pending.size() - for path in queue.pending: - if queue.is_ready(path): - remaining -= 1 - if remaining == 0: - get_tree().change_scene_to(queue.get_resource(target_scene)) - target_scene = null + if not netgame.gotm_mode: + if target_scene != null: + var remaining = queue.pending.size() + for path in queue.pending: + if queue.is_ready(path): + remaining -= 1 + if remaining == 0: + get_tree().change_scene_to(queue.get_resource(target_scene)) + target_scene = null func get_progress() -> String: if loading_text != null: diff --git a/Scenes/Loader.tscn b/Scenes/Loader.tscn index 308fa1c..3358828 100644 --- a/Scenes/Loader.tscn +++ b/Scenes/Loader.tscn @@ -4,7 +4,7 @@ [ext_resource path="res://Graphics/UI/iosevka-aile-regular.ttf" type="DynamicFontData" id=2] [ext_resource path="res://Scenes/Loader.gd" type="Script" id=3] -[sub_resource type="Shader" id=2] +[sub_resource type="Shader" id=1] code = "shader_type canvas_item; uniform vec2 tex_size; @@ -14,23 +14,22 @@ void fragment() { vec2 uv_adjusted = (UV - (uv_rect.xy / tex_size)) * (tex_size / uv_rect.zw); float dist = distance(uv_adjusted, vec2(0.5)); if (dist < 0.26) { - COLOR = vec4(1); + COLOR = vec4(1.); } else { COLOR = texture(TEXTURE, UV); } }" -custom_defines = "" -[sub_resource type="ShaderMaterial" id=3] -shader = SubResource( 2 ) +[sub_resource type="ShaderMaterial" id=2] +shader = SubResource( 1 ) shader_param/tex_size = Vector2( 240, 180 ) shader_param/uv_rect = Plane( 126, 16, 82, 84 ) -[sub_resource type="AtlasTexture" id=1] +[sub_resource type="AtlasTexture" id=3] atlas = ExtResource( 1 ) region = Rect2( 126, 16, 82, 84 ) -[sub_resource type="Shader" id=5] +[sub_resource type="Shader" id=4] code = "shader_type canvas_item; uniform vec2 tex_size; @@ -47,23 +46,21 @@ void fragment() { COLOR = vec4(tex.aaa, 1.-tex.a); } }" -custom_defines = "" -[sub_resource type="ShaderMaterial" id=6] -shader = SubResource( 5 ) +[sub_resource type="ShaderMaterial" id=5] +shader = SubResource( 4 ) shader_param/tex_size = Vector2( 240, 180 ) shader_param/uv_rect = Plane( 146, 39, 39, 38 ) -[sub_resource type="AtlasTexture" id=4] +[sub_resource type="AtlasTexture" id=6] atlas = ExtResource( 1 ) region = Rect2( 146, 39, 39, 38 ) -[sub_resource type="DynamicFont" id=8] +[sub_resource type="DynamicFont" id=7] size = 32 font_data = ExtResource( 2 ) -[sub_resource type="Animation" id=7] -resource_name = "spinner" +[sub_resource type="Animation" id=8] length = 7.0 loop = true tracks/0/type = "value" @@ -102,26 +99,26 @@ __meta__ = { } [node name="logo-temp-pixel2" type="TextureRect" parent="BottomRight"] -material = SubResource( 3 ) +material = SubResource( 2 ) margin_left = -7.11853 margin_top = -6.38116 margin_right = 92.8815 margin_bottom = 93.6188 rect_pivot_offset = Vector2( 50, 50 ) -texture = SubResource( 1 ) +texture = SubResource( 3 ) stretch_mode = 4 __meta__ = { "_edit_use_anchors_": false } [node name="logo-temp-pixel" type="TextureRect" parent="BottomRight"] -material = SubResource( 6 ) +material = SubResource( 5 ) margin_left = -1.5 margin_top = -1.0 margin_right = 88.5 margin_bottom = 89.0 rect_pivot_offset = Vector2( 79, 59 ) -texture = SubResource( 4 ) +texture = SubResource( 6 ) stretch_mode = 4 __meta__ = { "_edit_use_anchors_": false @@ -133,10 +130,10 @@ margin_top = 63.0 margin_right = 174.0 margin_bottom = 104.0 rect_scale = Vector2( 0.5, 0.5 ) -custom_fonts/font = SubResource( 8 ) +custom_fonts/font = SubResource( 7 ) custom_colors/font_color = Color( 1, 1, 1, 1 ) align = 2 [node name="AnimationPlayer" type="AnimationPlayer" parent="."] autoplay = "spinner" -anims/spinner = SubResource( 7 ) +anims/spinner = SubResource( 8 ) diff --git a/Scenes/Menu.gd b/Scenes/Menu.gd index 90cd2ec..6df077d 100644 --- a/Scenes/Menu.gd +++ b/Scenes/Menu.gd @@ -21,6 +21,10 @@ func _ready() -> void: $"/root/Music/BGM".play() netgame.connect("ms_updated", self, "_ms_update") request_servers() + if netgame.gotm_mode: + # Hide manual connect + $Popup/MarginContainer/VBoxContainer/Label2.visible = false + $Popup/MarginContainer/VBoxContainer/HBoxContainer.visible = false func _process(delta: float) -> void: refresh_server_remaining -= delta @@ -45,9 +49,14 @@ func _ms_update(action, result): if action == "list_games": # Reset server list server_list.clear() - servers = result - for server in servers: - server_list.add_item(server.data.name + " (" + server.address + ") - " + str(server.data.players) + "/" + str(server.data.max_players) + " players") + if netgame.gotm_mode: + servers = result + for server in servers: + server_list.add_item(server.name + " (" + server.id + ") - " + str(server.peers.size()) + "/" + str(server.get_property("max_players")) + " players") + else: + servers = result + for server in servers: + server_list.add_item(server.data.name + " (" + server.address + ") - " + str(server.data.players) + "/" + str(server.data.max_players) + " players") func set_scale(val) -> void: scale = val @@ -67,9 +76,9 @@ func _host_pressed() -> void: func _join_pressed() -> void: $Popup.popup_centered_ratio() -func join_server(addr: String) -> void: +func join_server(server) -> void: $"/root/Music/BGM".stop() - $"/root/Multiplayer".join(addr) + netgame.join(server) func _server_addr_changed(new_text: String) -> void: $Popup/MarginContainer/VBoxContainer/HBoxContainer/Button.disabled = new_text.length() < 1 @@ -78,4 +87,5 @@ func _manual_join_pressed(): join_server($Popup/MarginContainer/VBoxContainer/HBoxContainer/LineEdit.text) func _server_item_clicked(index): - join_server(servers[index].address) + $"/root/Music/BGM".stop() + join_server(servers[index]) diff --git a/Scenes/Menu.tscn b/Scenes/Menu.tscn index b68e5b6..1262121 100644 --- a/Scenes/Menu.tscn +++ b/Scenes/Menu.tscn @@ -168,6 +168,7 @@ custom_fonts/font = SubResource( 6 ) text = "Join an existing game" [node name="Popup" type="PopupDialog" parent="."] +visible = true anchor_right = 1.0 anchor_bottom = 1.0 margin_left = 100.0 diff --git a/export_presets.cfg b/export_presets.cfg index fae577f..fe7e0c0 100644 --- a/export_presets.cfg +++ b/export_presets.cfg @@ -43,7 +43,7 @@ application/trademarks="" [preset.1] -name="HTML5" +name="GOTM" platform="HTML5" runnable=true custom_features="" diff --git a/gotm/Gotm.gd b/gotm/Gotm.gd new file mode 100644 index 0000000..e68ab21 --- /dev/null +++ b/gotm/Gotm.gd @@ -0,0 +1,117 @@ +# MIT License +# +# Copyright (c) 2020-2020 Macaroni Studios AB +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +extends Node +#warnings-disable + + +# Official GDScript API for games on gotm.io +# This plugin serves as a polyfill while developing against the API locally. + +# The 'real' API calls are only available when running the game live on gotm.io. +# Running the game in the web player (gotm.io/web-player) also counts as live. + +# Add this script as a global autoload. Make sure the global autoload is named +# "Gotm". It must be named "Gotm" for it to work. + + +############################################################## +# SIGNALS +############################################################## +# You connected or disconnected from a lobby. Access it at 'Gotm.lobby' +signal lobby_changed() + +# Files were drag'n'dropped into the screen. +# The 'files' argument is an array of 'GotmFile'. +signal files_dropped(files, screen) + + + +############################################################## +# PROPERTIES +############################################################## +# These are all read only. + +# Player information. +var user: GotmUser = GotmUser.new() + +# Current lobby you are in. +# Is null when not in a lobby. +var lobby: GotmLobby = null + + +############################################################## +# METHODS +############################################################## + +# The API is live when the game runs on gotm.io. +# Running the game in the web player (gotm.io/web-player) also counts as live. +func is_live() -> bool: + return false + + +# Create a new lobby and join it. +# +# If 'show_invitation' is true, show an invitation link in a popup. +# +# By default, the lobby is hidden and is only accessible directly through +# its 'invite_link'. +# Set 'lobby.hidden' to false to make it fetchable with 'GotmLobbyFetch'. +# +# Returns the hosted lobby (also accessible at 'Gotm.lobby'). +static func host_lobby(show_invitation: bool = true) -> GotmLobby: + return _GotmImpl._host_lobby(GotmLobby.new()) + + +# Play an audio snippet with 'message' as a synthesized voice. +# 'language' is in BCP 47 format (e.g. "en-US" for american english). +# If specified language is not available "en-US" is used. +# Return true if playback succeeded. +func text_to_speech(message: String, language: String = "en-US") -> bool: + return true # pretend it worked + + +# Asynchronously open up the browser's file picker. +# +# If 'types' is specified, limit the file picker to files with matching file +# types (https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers). +# If 'only_one' is true, only allow the user to pick one file. +# +# If a picking-session is already in progress, an empty +# array is asynchronously returned. +# +# Asynchronously return an array of 'GotmFile'. +# Use 'yield(pick_files(), "completed")' to retrieve the return value. +func pick_files(types: Array = Array(), only_one: bool = false) -> Array: + yield(get_tree().create_timer(0.25), "timeout") + return [] + + + +############################################################## +# PRIVATE +############################################################## +func _ready() -> void: + _GotmImpl._initialize(GotmLobby, GotmUser) +func _process(delta) -> void: + _GotmImpl._process() +var _impl: Dictionary = {} diff --git a/gotm/GotmDebug.gd b/gotm/GotmDebug.gd new file mode 100644 index 0000000..1c14f1f --- /dev/null +++ b/gotm/GotmDebug.gd @@ -0,0 +1,65 @@ +# MIT License +# +# Copyright (c) 2020-2020 Macaroni Studios AB +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +class_name GotmDebug +#warnings-disable + +# Helper library for testing against the API locally, as if it would be live. +# +# These functions do not make real API calls. They fake operations and +# trigger relevant signals as if they happened live. +# +# These functions do nothing when the game is running live on gotm.io. +# Running the game in the web player (gotm.io/web-player) also counts as live. + +# Host a lobby without joining it. +# Note that the lobby is hidden by default and not fetchable with +# 'GotmLobbyFetch'. To make it fetchable, set 'hidden' to false. +# The lobby is only fetchable and joinable in this game process. +# Returns added lobby. +static func add_lobby() -> GotmLobby: + return _GotmDebugImpl._add_lobby(GotmLobby.new()) + + +# Remove a lobby created with 'add_lobby', as if its host (you) disconnected from it. +# Triggers 'lobby_changed' if you are in that lobby. +static func remove_lobby(lobby: GotmLobby) -> void: + _GotmDebugImpl._remove_lobby(lobby) + + +# Remove all lobbies. +static func clear_lobbies() -> void: + _GotmDebugImpl._clear_lobbies() + + +# Add yourself to the lobby, without joining it. +# Triggers 'peer_joined' if you are in that lobby. +# Returns joined peer. +static func add_lobby_peer(lobby: GotmLobby) -> GotmUser: + return _GotmDebugImpl._add_lobby_player(lobby, GotmUser.new()) + + +# Remove a peer created with 'add_lobby_peer' from the lobby, as if the peer (you) disconnected +# from the lobby. +# Triggers 'peer_left' if you are in that lobby. +static func remove_lobby_peer(lobby: GotmLobby, peer: GotmUser) -> void: + _GotmDebugImpl._remove_lobby_player(lobby, peer) diff --git a/gotm/GotmFile.gd b/gotm/GotmFile.gd new file mode 100644 index 0000000..c5938e6 --- /dev/null +++ b/gotm/GotmFile.gd @@ -0,0 +1,50 @@ +# MIT License +# +# Copyright (c) 2020-2020 Macaroni Studios AB +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +class_name GotmFile +#warnings-disable + +# A simple in-memory file descriptor used by 'Gotm.pick_files' and +# 'Gotm.files_dropped'. + + + +############################################################## +# PROPERTIES +############################################################## +# File name. +var name: String + +# File data. +var data: PoolByteArray + +# Last time the file was modified in unix time (seconds since epoch). +var modified_time: int + + + +############################################################## +# METHODS +############################################################## +# Save the file to the browser's download folder. +func download() -> void: + pass diff --git a/gotm/GotmLobby.gd b/gotm/GotmLobby.gd new file mode 100644 index 0000000..90656e6 --- /dev/null +++ b/gotm/GotmLobby.gd @@ -0,0 +1,155 @@ +# MIT License +# +# Copyright (c) 2020-2020 Macaroni Studios AB +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +class_name GotmLobby +#warnings-disable + +# A lobby is a way of connecting players with eachother as if they +# were on the same local network. +# +# Lobbies can be joined either directly through an 'invite_link', or by +# joining lobbies fetched with the 'GotmLobbyFetch' class. + +############################################################## +# SIGNALS +############################################################## +# Peer joined the lobby. +# 'peer_user' is a 'GotmUser' instance. +# This is only emitted if you are in this lobby. +signal peer_joined(peer_user) + +# Peer left the lobby. +# 'peer_user' is a 'GotmUser' instance. +# This is only emitted if you are in this lobby. +signal peer_left(peer_user) + + + +############################################################## +# READ-ONLY PROPERTIES +############################################################## +# Globally unique identifier. +var id: String + +# Other peers in the lobby with addresses. +# Is an array of 'GotmUser'. +var peers: Array = [] + +# You with address. +var me: GotmUser = GotmUser.new() + +# Host user with address. +var host: GotmUser = GotmUser.new() + +# Peers can join the lobby directly through this link. +var invite_link: String + + + +############################################################## +# WRITABLE PROPERTIES +############################################################## +# Note that only the host can write to these properties. + +# Name that is searchable using 'GotmLobbyFetch' +# Names longer than 64 characters are truncated. +var name: String = "" + +# Prevent the lobby from showing up in fetches? +# Peers may still join directly through 'invite_link' +var hidden: bool = true + +# Prevent new peers from joining? +# Also prevents the lobby from showing up in fetches. +var locked: bool = false + + + +############################################################## +# METHODS +############################################################## +# Asynchronously join this lobby after leaving current lobby. +# +# Use 'var success = yield(lobby.join(), "completed")' to wait for the call to complete +# and retrieve the return value. +# +# Sets 'Gotm.lobby' to the joined lobby if successful. +# +# Asyncronously returns true if successful, else false. +func join() -> bool: + return yield(_GotmImpl._join_lobby(self), "completed") + + +# Leave this lobby. +func leave() -> void: + _GotmImpl._leave_lobby(self) + + +# Am I the host of this lobby? +func is_host() -> bool: + return _GotmImpl._is_lobby_host(self) + + +# Get a custom property. +func get_property(name: String): + return _GotmImpl._get_lobby_property(self, name) + + + +################################ +# Host-only methods +################################ +# Kick peer from this lobby. +# Returns true if successful, else false. +func kick(peer: GotmUser) -> bool: + return _GotmImpl._kick_lobby_peer(self, peer) + + +# Store up to 10 of your own custom properties in the lobby. +# These are visible to other peers when fetching lobbies. +# Only properties of types String, int, float or bool are allowed. +# Integers are converted to floats. +# Strings longer than 64 characters are truncated. +# Setting 'value' to null removes the property. +func set_property(name: String, value) -> void: + _GotmImpl._set_lobby_property(self, name, value) + + +# Make this lobby filterable by a custom property. +# Filtering is done when fetching lobbies with 'GotmLobbyFetch'. +# Up to 3 properties can be set as filterable at once. +func set_filterable(property_name: String, filterable: bool = true) -> void: + _GotmImpl._set_lobby_filterable(self, property_name, filterable) + + +# Make this lobby sortable by a custom property. +# Sorting is done when fetching lobbies with 'GotmLobbyFetch'. +# Up to 3 properties can be set as sortable at once. +func set_sortable(property_name: String, sortable: bool = true) -> void: + _GotmImpl._set_lobby_sortable(self, property_name, sortable) + + + +################################ +# PRIVATE +################################ +var _impl: Dictionary = {} diff --git a/gotm/GotmLobbyFetch.gd b/gotm/GotmLobbyFetch.gd new file mode 100644 index 0000000..9f3748f --- /dev/null +++ b/gotm/GotmLobbyFetch.gd @@ -0,0 +1,122 @@ +# MIT License +# +# Copyright (c) 2020-2020 Macaroni Studios AB +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +class_name GotmLobbyFetch +#warnings-disable + +# Used for fetching non-hidden and non-locked lobbies. + + + +############################################################## +# PROPERTIES +############################################################## +################ +# Filter options +################ +# If not empty, fetch lobbies whose 'Lobby.name' contains 'name'. +var filter_name: String = "" + +# If not empty, fetch lobbies whose filterable custom properties +# matches those in 'filter_properties'. +# +# For example, setting 'filter_properties.difficulty = 2' will +# only fetch lobbies that have been set up with both 'lobby.set_property("difficulty", 2)' +# and 'lobby.set_filterable("difficulty", true)'. +# +# If your lobby has multiple filterable props, you must provide every filterable +# prop in 'filter_properties'. Setting a prop's value to 'null' will match any +# value of that prop. +var filter_properties: Dictionary = {} + + +################ +# Sort options +################ +# If not empty, sort by a sortable custom property. +# +# For example, setting 'sort_property = "difficulty"' will +# only fetch lobbies that have been set up with both 'lobby.set_property("difficulty", some_value)' +# and 'lobby.set_sortable("difficulty", true)'. +# +# If your lobby has a sortable prop, you must always provide a 'sort_property'. +var sort_property: String = "" + +# Sort results in ascending order? +var sort_ascending: bool = false + +# If not null, fetch lobbies whose sort property's value is equal to or greater than 'sort_min'. +var sort_min = null + +# If not null, fetch lobbies whose sort property's value is equal to or lesser than 'sort_max'. +var sort_max = null + +# If true, and 'sort_min' is provided, exclude lobbies whose sort property's value is equal to 'sort_min'. +var sort_min_exclusive = false + +# If true, and 'sort_max' is provided, exclude lobbies whose sort property's value is equal to 'sort_max'. +var sort_max_exclusive = false + + + +############################################################## +# METHODS +############################################################## +# All these methods asynchronously fetch up to 8 non-hidden +# and non-locked lobbies. +# +# Modifying any filtering or sorting option resets the state of this +# 'GotmLobbyFetch' instance and causes the next fetch call to +# fetch the first lobbies. +# +# All calls asynchronously return an array of fetched lobbies. +# Use 'yield(fetch.next(), "completed")' to retrieve it. + + +# Fetch the next lobbies, starting after the last lobby fetched +# in the previous call. +func next(count: int = 8) -> Array: + return yield(_GotmImpl._fetch_lobbies(self, count, "next"), "completed") + + +# Fetch the previous lobbies, ending before the first lobby +# that was fetched in the previous call. +func previous(count: int = 8) -> Array: + return yield(_GotmImpl._fetch_lobbies(self, count, "previous"), "completed") + + +# Fetch the first lobbies. +func first(count: int = 8) -> Array: + return yield(_GotmImpl._fetch_lobbies(self, count, "first"), "completed") + + +# Fetch lobbies at the current position. +# Useful for refreshing lobbies without changing the page. +func current(count: int = 8) -> Array: + return yield(_GotmImpl._fetch_lobbies(self, count, "current"), "completed") + + + +############################################################## +# PRIVATE +############################################################## +var _impl: Dictionary = {} diff --git a/gotm/GotmUser.gd b/gotm/GotmUser.gd new file mode 100644 index 0000000..e4a5021 --- /dev/null +++ b/gotm/GotmUser.gd @@ -0,0 +1,52 @@ +# MIT License +# +# Copyright (c) 2020-2020 Macaroni Studios AB +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +class_name GotmUser +#warnings-disable + +# Holds information about a Gotm user. + + + +############################################################## +# PROPERTIES +############################################################## +# These are all read-only. + +# Globally unique ID. +# Is empty if user is not logged in. +var id: String = "" + +# Current nickname. +var display_name: String = "" + +# The IP address of the user. +# Is empty if you are not in the same lobby. +var address: String = "" + +# Is user logged in? +var is_logged_in: bool = false + +############################################################## +# PRIVATE +############################################################## +var _impl: Dictionary = {} diff --git a/gotm/LICENSE b/gotm/LICENSE new file mode 100644 index 0000000..8195d8a --- /dev/null +++ b/gotm/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2020 Macaroni Studios AB + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/gotm/README.md b/gotm/README.md new file mode 100644 index 0000000..caf63d6 --- /dev/null +++ b/gotm/README.md @@ -0,0 +1,30 @@ +
+
+
+ Access Gotm's API with GDScript
+
+ Learn more
+