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

Commit 43e8614

Browse files
committed
Overhaul bomber tutorial
1 parent ba8c179 commit 43e8614

File tree

107 files changed

+3102
-359
lines changed

Some content is hidden

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

107 files changed

+3102
-359
lines changed

Diff for: .gitattributes

-7
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,6 @@
2929
*.webm filter=lfs diff=lfs merge=lfs -text
3030
*.zip filter=lfs diff=lfs merge=lfs -text
3131

32-
# Godot
33-
*.tscn filter=lfs diff=lfs merge=lfs -text
34-
*.tres filter=lfs diff=lfs merge=lfs -text
35-
*.scn filter=lfs diff=lfs merge=lfs -text
36-
*.res filter=lfs diff=lfs merge=lfs -text
37-
*.import filter=lfs diff=lfs merge=lfs -text
38-
3932
# Unity
4033
*.anim filter=lfs diff=lfs merge=lfs -text
4134
*.asset filter=lfs diff=lfs merge=lfs -text

Diff for: .gitignore

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,12 @@
88
# Godot-specific ignores
99
.import/
1010
export.cfg
11-
export_presets.cfg
11+
12+
# Godot 4.1 can let export_presets be commited. This can be changed on an
13+
# example level if any future examples work with Godot <4.1.
14+
# https://github.com/github/gitignore/pull/2827
15+
# https://github.com/godotengine/godot/pull/76165
16+
# export_presets.cfg
1217

1318
# Imported translations (automatically generated from CSV files)
1419
*.translation

Diff for: godot/bomber/Dockerfile

+10-22
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,21 @@
1-
FROM ubuntu:22.04 AS builder
2-
3-
# Install Godot & templates
4-
ENV GODOT_VERSION="4.0.2"
5-
RUN apt update -y \
6-
&& apt install -y wget unzip \
7-
&& wget https://downloads.tuxfamily.org/godotengine/${GODOT_VERSION}/Godot_v${GODOT_VERSION}-stable_linux.x86_64.zip \
8-
&& wget https://downloads.tuxfamily.org/godotengine/${GODOT_VERSION}/Godot_v${GODOT_VERSION}-stable_export_templates.tpz
9-
10-
RUN mkdir -p ~/.cache ~/.config/godot ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable \
11-
&& unzip Godot_v${GODOT_VERSION}-stable_linux.x86_64.zip \
12-
&& mv Godot_v${GODOT_VERSION}-stable_linux.x86_64 /usr/local/bin/godot \
13-
&& unzip Godot_v${GODOT_VERSION}-stable_export_templates.tpz \
14-
&& mv templates/* ~/.local/share/godot/export_templates/${GODOT_VERSION}.stable \
15-
&& rm Godot_v${GODOT_VERSION}-stable_export_templates.tpz Godot_v${GODOT_VERSION}-stable_linux.x86_64.zip
16-
17-
# Build application
1+
# MARK: Builder
2+
FROM ghcr.io/rivet-gg/godot-docker/godot:4.2 AS builder
183
WORKDIR /app
194
COPY . .
205
RUN mkdir -p build/linux \
21-
&& godot -v --export-release "Linux/X11" --headless ./build/linux/game.x86_64
22-
23-
# ===
6+
&& godot -v --export-release "Linux/X11" ./build/linux/game.x86_64 --headless
247

8+
# MARK: Runner
259
FROM ubuntu:22.04
2610
RUN apt update -y \
2711
&& apt install -y expect-dev \
28-
&& rm -rf /var/lib/apt/lists/*
12+
&& rm -rf /var/lib/apt/lists/* \
13+
&& useradd -ms /bin/bash rivet
14+
2915
COPY --from=builder /app/build/linux/ /app
3016

17+
# Change to user rivet
18+
USER rivet
19+
3120
# Unbuffer output so the logs get flushed
3221
CMD ["sh", "-c", "unbuffer /app/game.x86_64 --verbose --headless -- --server | cat"]
33-

Diff for: godot/bomber/LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2024 Rivet
3+
Copyright (c) 2023 Rivet
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

Diff for: godot/bomber/README.md

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
# Bomber
1+
# Multiplayer Bomber
22

3-
<p align="center">
4-
<img src="./_media/preview_512.png" />
5-
</p>
3+
A multiplayer implementation of the classical bomberman game.
4+
One of the players should press "host", while the other
5+
should type in his address and press "play".
66

7+
Language: GDScript
78

8-
[Visit Tutorial](https://rivet.gg/learn/godot/tutorials/crash-course)
9+
Renderer: GLES 2
910

11+
Check out this demo on the asset library: https://godotengine.org/asset-library/asset/139
1012

1113

1214

@@ -31,3 +33,4 @@
3133

3234
[Documentation](https://rivet.gg/learn/godot/tutorials/crash-course#step-4-deploy-to-rivet)
3335

36+
![Screenshot](screenshots/bomber.png)

Diff for: godot/bomber/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/bomber/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/bomber/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

0 commit comments

Comments
 (0)