Skip to content
This repository was archived by the owner on Dec 13, 2024. It is now read-only.

Commit 9c451bb

Browse files
committed
Add godot ball example
1 parent 4cc223d commit 9c451bb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+1932
-0
lines changed

Diff for: godot/bomber/project.godot

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ gamestate="*res://scripts/gamestate.gd"
2424
RivetHelper="*res://addons/rivet/rivet_helper.gd"
2525
Rivet="*res://addons/rivet/rivet_global.gd"
2626
RivetGlobal="*res://addons/rivet/rivet_global.gd"
27+
RivetClient="*res://addons/rivet/rivet_client.gd"
2728

2829
[display]
2930

Diff for: godot/circles/.gitattributes

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Normalize EOL for all files that Git considers text files.
2+
* text=auto eol=lf

Diff for: godot/circles/.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Godot 4+ specific ignores
2+
.godot/

Diff for: godot/circles/Dockerfile

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
FROM ghcr.io/rivet-gg/godot-docker/godot:4.2.1 AS builder
2+
WORKDIR /app
3+
COPY . .
4+
RUN mkdir -p build/linux \
5+
&& godot -v --export-release "Linux/X11" ./build/linux/game.x86_64 --headless
6+
7+
FROM ubuntu:22.04
8+
RUN apt update -y \
9+
&& apt install -y expect-dev \
10+
&& rm -rf /var/lib/apt/lists/* \
11+
&& useradd -ms /bin/bash rivet
12+
13+
COPY --from=builder /app/build/linux/ /app
14+
15+
# Change to user rivet
16+
USER rivet
17+
18+
# Unbuffer output so the logs get flushed
19+
CMD ["sh", "-c", "unbuffer /app/game.x86_64 --verbose --headless -- --server | cat"]

Diff for: godot/circles/addons/rivet/api/rivet_api.gd

+132
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
class_name RivetApi
2+
const RivetRequest = preload("rivet_request.gd")
3+
4+
static var CONFIGURATION_CACHE
5+
6+
static func _get_configuration():
7+
if CONFIGURATION_CACHE:
8+
return CONFIGURATION_CACHE
9+
10+
if FileAccess.file_exists(RivetPluginBridge.RIVET_CONFIGURATION_FILE_PATH):
11+
var config_file = ResourceLoader.load(RivetPluginBridge.RIVET_CONFIGURATION_FILE_PATH)
12+
if config_file and 'new' in config_file:
13+
CONFIGURATION_CACHE = config_file.new()
14+
return CONFIGURATION_CACHE
15+
16+
if FileAccess.file_exists(RivetPluginBridge.RIVET_DEPLOYED_CONFIGURATION_FILE_PATH):
17+
var deployed_config_file = ResourceLoader.load(RivetPluginBridge.RIVET_DEPLOYED_CONFIGURATION_FILE_PATH)
18+
if deployed_config_file and 'new' in deployed_config_file:
19+
CONFIGURATION_CACHE = deployed_config_file.new()
20+
return CONFIGURATION_CACHE
21+
22+
push_warning("Rivet configuration file not found")
23+
CONFIGURATION_CACHE = null
24+
return CONFIGURATION_CACHE
25+
26+
static func _get_api_url():
27+
# Use plugin config if available
28+
var plugin = RivetPluginBridge.get_plugin()
29+
if plugin:
30+
return plugin.api_endpoint
31+
32+
# Override shipped configuration endpoint
33+
var url_env = OS.get_environment("RIVET_API_ENDPOINT")
34+
if url_env:
35+
return url_env
36+
37+
# Use configuration shipped with game
38+
var config = _get_configuration()
39+
if config:
40+
return config.api_endpoint
41+
42+
# Fallback
43+
return "https://api.rivet.gg"
44+
45+
## Get authorization token used from within only the plugin for cloud-specific
46+
## actions.
47+
static func _get_cloud_token():
48+
# Use plugin config if available
49+
var plugin = RivetPluginBridge.get_plugin()
50+
if plugin:
51+
return plugin.cloud_token
52+
53+
OS.crash("Rivet cloud token not found, this should only be called within the plugin")
54+
55+
## Get authorization token used for making requests from within the game.
56+
##
57+
## The priority of tokens is:
58+
##
59+
## - If in editor, use the plugin token
60+
## - If provided by environment, then use that (allows for testing)
61+
## - Assume config is provided by the game client
62+
static func _get_runtime_token():
63+
# Use plugin config if available
64+
var plugin = RivetPluginBridge.get_plugin()
65+
if plugin:
66+
return plugin.namespace_token
67+
68+
# Use configuration shipped with game
69+
var token_env = OS.get_environment("RIVET_TOKEN")
70+
if token_env:
71+
return token_env
72+
73+
# Use configuration shipped with game
74+
var config = _get_configuration()
75+
if config:
76+
return config.namespace_token
77+
78+
OS.crash("Rivet token not found, validate a config is shipped with the game in the .rivet folder")
79+
80+
## Builds the headers for a request, including the authorization token
81+
static func _build_headers(service: String) -> PackedStringArray:
82+
var token = _get_cloud_token() if service == "cloud" else _get_runtime_token()
83+
return [
84+
"Authorization: Bearer " + token,
85+
]
86+
87+
## Builds a URL to Rivet cloud services
88+
static func _build_url(path: String, service: String) -> String:
89+
var path_segments := path.split("/", false)
90+
path_segments.remove_at(0)
91+
return _get_api_url() + "/%s/%s" % [service, "/".join(path_segments)]
92+
93+
## Gets service name from a path (e.g. /users/123 -> users)
94+
static func _get_service_from_path(path: String) -> String:
95+
var path_segments := path.split("/", false)
96+
return path_segments[0]
97+
98+
## Creates a POST request to Rivet cloud services
99+
## @experimental
100+
static func POST(owner: Node, path: String, body: Dictionary) -> RivetRequest:
101+
var service := _get_service_from_path(path)
102+
var url := _build_url(path, service)
103+
var body_json := JSON.stringify(body)
104+
105+
return RivetRequest.new(owner, HTTPClient.METHOD_POST, url, {
106+
"headers": _build_headers(service),
107+
"body": body_json
108+
})
109+
110+
## Creates a GET request to Rivet cloud services
111+
## @experimental
112+
static func GET(owner: Node, path: String, body: Dictionary) -> RivetRequest:
113+
var service := _get_service_from_path(path)
114+
var url := _build_url(path, service)
115+
var body_json := JSON.stringify(body)
116+
117+
return RivetRequest.new(owner, HTTPClient.METHOD_GET, url, {
118+
"headers": _build_headers(service),
119+
"body": body_json
120+
})
121+
122+
## Creates a PUT request to Rivet cloud services
123+
## @experimental
124+
static func PUT(owner: Node, path: String, body: Dictionary) -> RivetRequest:
125+
var service := _get_service_from_path(path)
126+
var url := _build_url(path, service)
127+
var body_json := JSON.stringify(body)
128+
129+
return RivetRequest.new(owner, HTTPClient.METHOD_PUT, url, {
130+
"headers": _build_headers(service),
131+
"body": body_json
132+
})

Diff for: godot/circles/addons/rivet/api/rivet_packages.gd

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
const _RivetResponse = preload("rivet_response.gd")
2+
3+
## Lobbies
4+
## @experimental
5+
class Lobbies:
6+
## Finds a lobby based on the given criteria. If a lobby is not found and
7+
## prevent_auto_create_lobby is true, a new lobby will be created.
8+
##
9+
## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/find}[/url]
10+
func find(body: Dictionary = {}):
11+
return await Rivet.POST("matchmaker/lobbies/find", body).wait_completed()
12+
13+
## Joins a specific lobby. This request will use the direct player count
14+
## configured for the lobby group.
15+
##
16+
## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/join}[/url]
17+
func join(body: Dictionary = {}) -> _RivetResponse:
18+
return await Rivet.POST("matchmaker/lobbies/join", body).wait_completed()
19+
20+
## Marks the current lobby as ready to accept connections. Players will not
21+
## be able to connect to this lobby until the lobby is flagged as ready.
22+
##
23+
## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/ready}[/url]
24+
func ready(body: Dictionary = {}) -> _RivetResponse:
25+
return await Rivet.POST("matchmaker/lobbies/ready", body).wait_completed()
26+
27+
## If is_closed is true, the matchmaker will no longer route players to the
28+
## lobby. Players can still join using the /join endpoint (this can be disabled
29+
## by the developer by rejecting all new connections after setting the lobby
30+
## to closed). Does not shutdown the lobby.
31+
##
32+
## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/set-closed}[/url]
33+
func setClosed(body: Dictionary = {}) -> _RivetResponse:
34+
return await Rivet.PUT("matchmaker/lobbies/set_closed", body).wait_completed()
35+
36+
## Creates a custom lobby.
37+
##
38+
## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/create}[/url]
39+
func create(body: Dictionary = {}) -> _RivetResponse:
40+
return await Rivet.POST("matchmaker/lobbies/create", body).wait_completed()
41+
42+
## Lists all open lobbies.
43+
##
44+
## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/list}[/url]
45+
func list(body: Dictionary = {}) -> _RivetResponse:
46+
return await Rivet.GET("matchmaker/lobbies/list", body).wait_completed()
47+
48+
##
49+
##
50+
## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/set-state}[/url]
51+
func setState(body: Dictionary = {}) -> _RivetResponse:
52+
return await Rivet.PUT("matchmaker/lobbies/state", body).wait_completed()
53+
54+
##
55+
##
56+
## [url]{https://rivet.gg/docs/matchmaker/api/lobbies/get-state}[/url]
57+
func getState(lobby_id, body: Dictionary = {}) -> _RivetResponse:
58+
return await Rivet.GET("matchmaker/lobbies/{lobby_id}/state".format({"lobby_id": lobby_id}), body).wait_completed()
59+
60+
## Players
61+
## @experimental
62+
class Players:
63+
## Validates the player token is valid and has not already been consumed then
64+
## marks the player as connected.
65+
##
66+
## [url]{https://rivet.gg/docs/matchmaker/api/players/connected}[/url]
67+
func connected(body: Dictionary = {}) -> _RivetResponse:
68+
return await Rivet.POST("matchmaker/players/connected", body).wait_completed()
69+
70+
## Marks a player as disconnected. # Ghost Players.
71+
##
72+
## [url]{https://rivet.gg/docs/matchmaker/api/players/disconnected}[/url]
73+
func disconnected(body: Dictionary = {}) -> _RivetResponse:
74+
return await Rivet.POST("matchmaker/players/disconnected", body).wait_completed()
75+
76+
## Gives matchmaker statistics about the players in game.
77+
##
78+
## [url]{https://rivet.gg/docs/matchmaker/api/players/statistics}[/url]
79+
func getStatistics(body: Dictionary = {}) -> _RivetResponse:
80+
return await Rivet.GET("matchmaker/players/statistics", body).wait_completed()
81+
82+
class Regions:
83+
## Returns a list of regions available to this namespace.
84+
## Regions are sorted by most optimal to least optimal.
85+
## The player's IP address is used to calculate the regions' optimality.
86+
##
87+
## [url]{https://rivet.gg/docs/matchmaker/api/regions/list}[/url]
88+
func list(body: Dictionary = {}) -> _RivetResponse:
89+
return await Rivet.GET("matchmaker/regions", body).wait_completed()
90+
91+
## Matchmaker
92+
## @experimental
93+
## @tutorial: https://rivet.gg/docs/matchmaker
94+
class Matchmaker:
95+
static var lobbies: Lobbies = Lobbies.new()
96+
static var players: Players = Players.new()
97+
static var regions: Regions = Regions.new()

Diff for: godot/circles/addons/rivet/api/rivet_request.gd

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
extends RefCounted
2+
## A wrapper around HTTPRequest that emits a signal when the request is completed.
3+
## This is a workaround for the fact that `HTTPRequest.request()` is blocking.
4+
## To run a request, create a new RivetRequest, connect to the completed signal,
5+
## and call `request().wait_completed()` to wait for the request to complete.
6+
7+
8+
const _RivetResponse := preload("rivet_response.gd")
9+
const _RivetRequest := preload("rivet_request.gd")
10+
11+
var response: _RivetResponse = null
12+
var _opts: Dictionary
13+
var _http_request: HTTPRequest
14+
15+
var _success_callback: Callable
16+
var _failure_callback: Callable
17+
18+
signal completed(response: _RivetResponse)
19+
signal succeeded(response: _RivetResponse)
20+
signal failed(response: _RivetResponse)
21+
22+
func _init(owner: Node, method: HTTPClient.Method, url: String, opts: Variant = null):
23+
self._http_request = HTTPRequest.new()
24+
self._http_request.request_completed.connect(_on_request_completed)
25+
self._opts = {
26+
"method": method,
27+
"url": url,
28+
"body": opts.body,
29+
"headers": opts.headers,
30+
}
31+
owner.add_child(self._http_request)
32+
self._http_request.request(_opts.url, _opts.headers, _opts.method, _opts.body)
33+
34+
func set_success_callback(callback: Callable) -> _RivetRequest:
35+
self._success_callback = callback
36+
return self
37+
38+
func set_failure_callback(callback: Callable) -> _RivetRequest:
39+
self._failure_callback = callback
40+
return self
41+
42+
func _on_request_completed(result, response_code, headers, body):
43+
self.response = _RivetResponse.new(result, response_code, headers, body)
44+
if result == OK:
45+
succeeded.emit(response)
46+
if self._success_callback:
47+
self._success_callback.call(response)
48+
else:
49+
failed.emit(response)
50+
if self._failure_callback:
51+
self._failure_callback.call(response)
52+
completed.emit(response)
53+
54+
## Waits for the request to complete and returns the response in non-blocking way
55+
func wait_completed() -> _RivetResponse:
56+
await completed
57+
return response

Diff for: godot/circles/addons/rivet/api/rivet_response.gd

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
extends RefCounted
2+
## A response from the server. Contains the result, response code, headers, and body.
3+
## The body is a dictionary of the JSON response.
4+
##
5+
## @experimental
6+
7+
## The result of the request. 0 is success, 1 is failure.
8+
var result: HTTPClient.Status
9+
10+
## The response code from the server.
11+
var response_code: HTTPClient.ResponseCode
12+
13+
## The headers from the server.
14+
var headers: PackedStringArray
15+
16+
## The body of the response, as a JSON dictionary, could be a null.
17+
var body: Variant
18+
19+
func _init(result: int, response_code: int, headers: PackedStringArray, response_body: PackedByteArray) -> void:
20+
self.result = result
21+
self.response_code = response_code
22+
self.headers = headers
23+
24+
var json = JSON.new()
25+
json.parse(response_body.get_string_from_utf8())
26+
body = json.get_data()
27+
+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
@tool extends MarginContainer
2+
3+
@onready var namespace_selector: OptionButton = %DeployNamespaceSelector
4+
@onready var manage_versions_button: Button = %ManageVersionButton
5+
@onready var build_deploy_button: Button = %BuildDeployButton
6+
7+
func _ready() -> void:
8+
manage_versions_button.pressed.connect(_on_manage_versions_button_pressed)
9+
build_deploy_button.pressed.connect(_on_build_deploy_button_pressed)
10+
11+
func _on_manage_versions_button_pressed() -> void:
12+
_all_actions_set_disabled(true)
13+
14+
var result = await RivetPluginBridge.get_plugin().cli.run_command(["sidekick", "get-version", "--namespace", namespace_selector.current_value.namespace_id])
15+
if result.exit_code != 0 or !("Ok" in result.output):
16+
RivetPluginBridge.display_cli_error(self, result)
17+
18+
OS.shell_open(result.output["Ok"]["output"])
19+
_all_actions_set_disabled(false)
20+
21+
func _on_build_deploy_button_pressed() -> void:
22+
_all_actions_set_disabled(true)
23+
24+
var result = await RivetPluginBridge.get_plugin().cli.run_command(["sidekick", "--show-terminal", "deploy", "--namespace", namespace_selector.current_value.name_id])
25+
if result.exit_code != 0:
26+
RivetPluginBridge.display_cli_error(self, result)
27+
28+
_all_actions_set_disabled(false)
29+
30+
func _all_actions_set_disabled(disabled: bool) -> void:
31+
namespace_selector.disabled = disabled
32+
manage_versions_button.disabled = disabled
33+
build_deploy_button.disabled = disabled
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:cc59f7a53362220abc3d4858dab74f6b459cd8c585ee759450b252e018031b23
3+
size 1840

0 commit comments

Comments
 (0)