summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-03-29 19:35:17 +0530
committerBobby <[email protected]>2026-03-29 19:35:17 +0530
commita3e669824509bf3c4f8bdf0f52cbf025be66a4f1 (patch)
tree4e54247260d9f697800318873c44227e636393f7
downloadechoes-of-vaelun-a3e669824509bf3c4f8bdf0f52cbf025be66a4f1.tar.xz
echoes-of-vaelun-a3e669824509bf3c4f8bdf0f52cbf025be66a4f1.zip
Add initial game setup and testing framework
- Created an SVG icon for the game. - Added project configuration file and main scene. - Implemented a basic main script for the game. - Established a test suite with base test functionality. - Integrated Nakama client for multiplayer features. - Developed various tests for client, socket, and storage functionalities. - Configured Docker Compose for local development with CockroachDB and Nakama.
-rw-r--r--.gitignore51
-rw-r--r--game/.editorconfig4
-rw-r--r--game/.gitattributes2
-rw-r--r--game/.github/hl-small-logo.pngbin0 -> 7678 bytes
-rw-r--r--game/.gitignore3
-rw-r--r--game/CHANGELOG.md130
-rw-r--r--game/LICENSE203
-rw-r--r--game/README.md320
-rw-r--r--game/addons/com.heroiclabs.nakama/Nakama.gd61
-rw-r--r--game/addons/com.heroiclabs.nakama/Nakama.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/api/NakamaAPI.gd6120
-rw-r--r--game/addons/com.heroiclabs.nakama/api/NakamaAPI.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd902
-rw-r--r--game/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd635
-rw-r--r--game/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/api/NakamaSession.gd157
-rw-r--r--game/addons/com.heroiclabs.nakama/api/NakamaSession.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd34
-rw-r--r--game/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd28
-rw-r--r--game/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/client/NakamaClient.gd1084
-rw-r--r--game/addons/com.heroiclabs.nakama/client/NakamaClient.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd208
-rw-r--r--game/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/dotnet-utils/GodotHttpAdapter.cs112
-rw-r--r--game/addons/com.heroiclabs.nakama/dotnet-utils/GodotLogger.cs79
-rw-r--r--game/addons/com.heroiclabs.nakama/dotnet-utils/GodotWebSocketAdapter.cs211
-rw-r--r--game/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd598
-rw-r--r--game/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd75
-rw-r--r--game/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd34
-rw-r--r--game/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/utils/NakamaException.gd42
-rw-r--r--game/addons/com.heroiclabs.nakama/utils/NakamaException.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd38
-rw-r--r--game/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd363
-rw-r--r--game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd97
-rw-r--r--game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd.uid1
-rw-r--r--game/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd160
-rw-r--r--game/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd.uid1
-rw-r--r--game/codegen/README.md21
-rw-r--r--game/codegen/main.go639
-rw-r--r--game/icon.svg1
-rw-r--r--game/icon.svg.import43
-rw-r--r--game/main.gd4
-rw-r--r--game/main.gd.uid1
-rw-r--r--game/main.tscn6
-rw-r--r--game/project.godot28
-rw-r--r--game/test_suite/base_test.gd133
-rw-r--r--game/test_suite/bin/.gitignore2
-rw-r--r--game/test_suite/project.godot119
-rw-r--r--game/test_suite/run_tests.sh22
-rw-r--r--game/test_suite/runner.gd15
-rw-r--r--game/test_suite/settings.json7
-rw-r--r--game/test_suite/tester.gd56
-rw-r--r--game/test_suite/tester.tscn12
-rw-r--r--game/test_suite/tests/client_test.gd32
-rw-r--r--game/test_suite/tests/socket_multi_test.gd79
-rw-r--r--game/test_suite/tests/socket_party_test.gd98
-rw-r--r--game/test_suite/tests/socket_simple_test.gd27
-rw-r--r--game/test_suite/tests/storage_test.gd72
-rw-r--r--game/test_suite/utils/config.gd22
-rw-r--r--toolchain/docker-compose.yml82
68 files changed, 13289 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a001c4f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,51 @@
+# ============================================
+# Echoes of Vaelun — Root .gitignore
+# ============================================
+
+# ── Godot ────────────────────────────────────
+game/.godot/
+game/export/
+game/*.log
+
+# Godot 4 import cache — regenerated automatically
+game/.import/
+
+# ── Nakama / Docker data ──────────────────────
+data/
+
+# ── OS garbage ───────────────────────────────
+.DS_Store
+Thumbs.db
+Desktop.ini
+$RECYCLE.BIN/
+
+# ── VS Code ───────────────────────────────────
+.vscode/
+
+# ── Claude AI ─────────────────────────────────
+.claude/
+
+# ── Python ────────────────────────────────────
+__pycache__/
+*.pyc
+*.pyo
+*.pyd
+.env
+venv/
+.venv/
+
+# ── Audio generation cache ────────────────────
+# Generated audio files tracked via Git LFS not ignored
+# scripts/generate_audio.py output goes to game/assets/audio/
+# which IS tracked — only caches ignored
+*.wav.cache
+*.ogg.cache
+
+# ── Logs ─────────────────────────────────────
+*.log
+logs/
+
+# ── Secrets ───────────────────────────────────
+.env
+.env.local
+secrets.cfg \ No newline at end of file
diff --git a/game/.editorconfig b/game/.editorconfig
new file mode 100644
index 0000000..f28239b
--- /dev/null
+++ b/game/.editorconfig
@@ -0,0 +1,4 @@
+root = true
+
+[*]
+charset = utf-8
diff --git a/game/.gitattributes b/game/.gitattributes
new file mode 100644
index 0000000..8ad74f7
--- /dev/null
+++ b/game/.gitattributes
@@ -0,0 +1,2 @@
+# Normalize EOL for all files that Git considers text files.
+* text=auto eol=lf
diff --git a/game/.github/hl-small-logo.png b/game/.github/hl-small-logo.png
new file mode 100644
index 0000000..4956222
--- /dev/null
+++ b/game/.github/hl-small-logo.png
Binary files differ
diff --git a/game/.gitignore b/game/.gitignore
new file mode 100644
index 0000000..0af181c
--- /dev/null
+++ b/game/.gitignore
@@ -0,0 +1,3 @@
+# Godot 4+ specific ignores
+.godot/
+/android/
diff --git a/game/CHANGELOG.md b/game/CHANGELOG.md
new file mode 100644
index 0000000..cf6c0f4
--- /dev/null
+++ b/game/CHANGELOG.md
@@ -0,0 +1,130 @@
+# Change Log
+All notable changes to this project are documented below.
+
+The format is based on [keep a changelog](http://keepachangelog.com/) and this project uses [semantic versioning](http://semver.org/).
+
+## [3.3.1] - 2023-04-17
+
+### Fixed
+
+- Fix arguments for HTTPRequest.request() for beta17
+- Fix typehints for enums in Godot 4.0-rc1
+- Fix type check and typehint for Godot 4.0-rc3
+- Fix null byte array error in GodotHttpAdapter for C#
+
+## [3.3.0] - 2023-01-30
+
+### Added
+
+- Add support for subscription validation APIs that were added in Nakama v3.13.0
+- Add support for sending events
+- Allow disabling threads for making HTTP requests
+- Add support for delete_account_sync() and other API changes that were added in Nakama v3.15.0
+
+## [3.2.0] - 2022-08-30
+
+### Fixed
+
+- Fix NakamaSocket.add_matchmaker_party_async() and the tests for it
+- Fix MatchData.op_code type in schema to TYPE_INT
+- Fix circular reference in Nakama singleton to itself
+
+### Added
+
+- Add support for receiving binary data in "NakamaRTAPI.MatchState"
+- Add support for sending and receiving binary data in "NakamaRTAPI.PartyData"
+- Add NakamaMultiplayerBridge to integrate with Godot's High-Level Multiplayer API
+
+## [3.1.0] - 2022-04-28
+
+### Added
+
+- Expose the "seen_before" property on "NakamaAPI.ApiValidatedPurchase"
+- Add support for creating match by name
+- Add support for "count_multple" on "NakamaSocket.add_matchmaker_async()" and "NakamaSocket.add_matchmaker_party_async()"
+- Add C# support classes to better integrate the .NET client with the Mono version of Godot, allowing HTML5 exports to work
+
+### Fixed
+
+- Fix receiving "NakamaRTAPI.PartyClose" message
+- Fix sending and receiving of PartyData
+
+## [3.0.0] - 2022-03-28
+
+### Added
+
+- Add realtime party support.
+- Add purchase validation functions.
+- Add Apple authentication functions.
+- Add "demote_group_users_async" function.
+- A session can be refreshed on demand with "session_refresh_async" method.
+- Session and/or refresh tokens can now be invalidated with a client logout.
+- The client now supports session auto-refresh using refresh tokens. This is enabled by default.
+- The client now supports auto-retrying failed request due to network error. This is enabled by defulut.
+- The client now support cancelling requests in-flight via "client.cancel_request".
+
+### Fixed
+
+- Fix Dictionary serialization (e.g. "NakamaSocket.add_matchmaker_async" "p_string_props" and "p_numeric_props").
+- Pass join metadata onwards into match join message.
+- Don't stop processing messages when the game is paused.
+- Fix "rpc_async", "rpc_async_with_key". Now uses GET request only if no payload is passed.
+- Fix client errors parsing in Nakama 3.x
+- Make it possible to omit the label and query on NakamaClient.list_matches_async().
+
+### Backwards incompatible changes
+
+- The "received_error" signal on "NakamaSocket" is now emited with an "NakamaRTAPI.Error" object received from the server.
+ Previously, it was emitted with an integer error code when the socket failed to connect.
+ If you have old code using the "received_error" signal, you can switch to the new "connection_error" signal, which was added to replace it.
+
+## [2.1.0] - 2020-08-01
+
+### Added
+
+- Add an optional log level parameter to "Nakama.create_client".
+
+### Changed
+
+- Update variable definitions to new gdscript variable controls.
+
+### Fixed
+
+- Fix "add_friends_async" should have its "id" field input as optional.
+- Fix "add_matchmaker_async" and "MatchmakerAdd" parameter assignment.
+- Fix missing "presence" property in NakamaRTAPI.MatchData.
+- Fix NakamaSocket not emitting "received_error" correctly.
+- Fix "DEFAULT_LOG_LEVEL" in Nakama.gd not doing anything.
+
+## [2.0.0] - 2020-04-02
+
+### Added
+
+- Decode base64 data in "MatchData". (breaks compat)
+- Add "FacebookInstantGame" endpoints (link/unlink/authenticate).
+- GDScript-style comments (removing all XML tags).
+- Add "list_storage_objects_async" "p_user_id" parameter to allow listing user(s) objects.
+
+### Fixed
+
+- Fix encoding of "op_code" in "MatchDataSend" and marshalling of "NakamaSocket.send_match_state_[raw_]async".
+- Fix parsing of "MatchmakerMatched" messages when no token is specified.
+- Disable "HTTPRequest.use_threads" in HTML5 exports.
+- "NakamaSession.is_expired" returned reversed result.
+- Fix "NakamaClient.update_account_async" to allow updating account without username change.
+- Fix "NakamaClient.update_group_async" to allow updating group without name change.
+- Fix "HTTPAdapter._send_async" error catching for some edge cases.
+- Fix "NakamaClient.send_rpc_async" with empty payload (will send empty string now).
+- Fix "NakamaRTAPI.Status" parsing.
+- Fix "NakamaClient" "list_leaderboard_records_around_owner_async" and "list_leaderboard_records_async" parameter order. (breaks compat)
+- Rename "NakamaClient.JoinTournamentAsync" to "join_tournament_async" for consistent naming.
+- Update all "p_limit" parameters default in "NakamaClient" to "10".
+- Fix "NakamaRTAPI.Stream" parsing.
+
+## [1.0.0] - 2020-01-28
+### Added
+- Initial public release.
+- Client API implementation.
+- Realtime Socket implementation.
+- Helper singleton.
+- Setup instructions.
diff --git a/game/LICENSE b/game/LICENSE
new file mode 100644
index 0000000..6b0b127
--- /dev/null
+++ b/game/LICENSE
@@ -0,0 +1,203 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/game/README.md b/game/README.md
new file mode 100644
index 0000000..ff0a26f
--- /dev/null
+++ b/game/README.md
@@ -0,0 +1,320 @@
+Nakama Godot
+===========
+
+> Godot client for Nakama server written in GDScript.
+
+[Nakama](https://github.com/heroiclabs/nakama) is an open-source server designed to power modern games and apps. Features include user accounts, chat, social, matchmaker, realtime multiplayer, and much [more](https://heroiclabs.com).
+
+This client implements the full API and socket options with the server. It's written in GDScript to support Godot Engine `4.0+`.
+
+Full documentation is online - https://heroiclabs.com/docs
+
+## Godot 3 & 4
+
+You're currently looking at the Godot 4 version of the Nakama client for Godot.
+
+If you are using Godot 3, you need to use the ['master'
+branch](https://github.com/heroiclabs/nakama-godot/tree/godot-4) on GitHub.
+
+## Getting Started
+
+You'll need to setup the server and database before you can connect with the client. The simplest way is to use Docker but have a look at the [server documentation](https://github.com/heroiclabs/nakama#getting-started) for other options.
+
+1. Install and run the servers. Follow these [instructions](https://heroiclabs.com/docs/install-docker-quickstart).
+
+2. Download the client from the [releases page](https://github.com/heroiclabs/nakama-godot/releases) and import it into your project. You can also [download it from the asset repository](#asset-repository).
+
+3. Add the `Nakama.gd` singleton (in `addons/com.heroiclabs.nakama/`) as an [autoload in Godot](https://docs.godotengine.org/en/stable/getting_started/step_by_step/singletons_autoload.html).
+
+4. Use the connection credentials to build a client object using the singleton.
+
+ ```gdscript
+ extends Node
+
+ func _ready():
+ var scheme = "http"
+ var host = "127.0.0.1"
+ var port = 7350
+ var server_key = "defaultkey"
+ var client := Nakama.create_client(server_key, host, port, scheme)
+ ```
+
+## Usage
+
+The client object has many methods to execute various features in the server or open realtime socket connections with the server.
+
+### Authenticate
+
+There's a variety of ways to [authenticate](https://heroiclabs.com/docs/authentication) with the server. Authentication can create a user if they don't already exist with those credentials. It's also easy to authenticate with a social profile from Google Play Games, Facebook, Game Center, etc.
+
+```gdscript
+ var email = "[email protected]"
+ var password = "batsignal"
+ # Use 'await' to wait for the request to complete.
+ var session : NakamaSession = await client.authenticate_email_async(email, password)
+ print(session)
+```
+
+### Sessions
+
+When authenticated the server responds with an auth token (JWT) which contains useful properties and gets deserialized into a `NakamaSession` object.
+
+```gdscript
+ print(session.token) # raw JWT token
+ print(session.user_id)
+ print(session.username)
+ print("Session has expired: %s" % session.expired)
+ print("Session expires at: %s" % session.expire_time)
+```
+
+It is recommended to store the auth token from the session and check at startup if it has expired. If the token has expired you must reauthenticate. The expiry time of the token can be changed as a setting in the server.
+
+```gdscript
+ var authtoken = "restored from somewhere"
+ var session2 = NakamaClient.restore_session(authtoken)
+ if session2.expired:
+ print("Session has expired. Must reauthenticate!")
+```
+
+NOTE: The length of the lifetime of a session can be changed on the server with the `--session.token_expiry_sec` command flag argument.
+
+### Requests
+
+The client includes lots of builtin APIs for various features of the game server. These can be accessed with the async methods. It can also call custom logic in RPC functions on the server. These can also be executed with a socket object.
+
+All requests are sent with a session object which authorizes the client.
+
+```gdscript
+ var account = await client.get_account_async(session)
+ print(account.user.id)
+ print(account.user.username)
+ print(account.wallet)
+```
+
+### Exceptions
+
+Since Godot Engine does not support exceptions, whenever you make an async request via the client or socket, you can check if an error occurred via the `is_exception()` method.
+
+```gdscript
+ var an_invalid_session = NakamaSession.new() # An empty session, which will cause and error when we use it.
+ var account2 = await client.get_account_async(an_invalid_session)
+ print(account2) # This will print the exception
+ if account2.is_exception():
+ print("We got an exception")
+```
+
+### Socket
+
+The client can create one or more sockets with the server. Each socket can have it's own event listeners registered for responses received from the server.
+
+```gdscript
+ var socket = Nakama.create_socket_from(client)
+ socket.connected.connect(self._on_socket_connected)
+ socket.closed.connect(self._on_socket_closed)
+ socket.received_error.connect(self._on_socket_error)
+ await socket.connect_async(session)
+ print("Done")
+
+func _on_socket_connected():
+ print("Socket connected.")
+
+func _on_socket_closed():
+ print("Socket closed.")
+
+func _on_socket_error(err):
+ printerr("Socket error %s" % err)
+```
+
+## Integration with Godot's High-level Multiplayer API
+
+Godot provides a [High-level Multiplayer
+API](https://docs.godotengine.org/en/latest/tutorials/networking/high_level_multiplayer.html),
+allowing developers to make RPCs, calling functions that run on other peers in
+a multiplayer match.
+
+For example:
+
+```gdscript
+func _process(delta):
+ if not is_multiplayer_authority():
+ return
+
+ var input_vector = get_input_vector()
+
+ # Move the player locally.
+ velocity = input_vector * SPEED
+ move_and_slide()
+
+ # Then update the player's position on all other connected clients.
+ update_remote_position.rpc(position)
+
+@rpc(any_peer)
+func update_remote_position(new_position):
+ position = new_position
+```
+
+Godot provides a number of built-in backends for sending the RPCs, including:
+ENet, WebSockets, and WebRTC.
+
+However, you can also use the Nakama client as a backend! This can allow you to
+continue using Godot's familiar High-level Multiplayer API, but with the RPCs
+transparently sent over a realtime Nakama match.
+
+To do that, you need to use the `NakamaMultiplayerBridge` class:
+
+```gdscript
+var multiplayer_bridge
+
+func _ready():
+ # [...]
+ # You must have a working 'socket', created as described above.
+
+ multiplayer_bridge = NakamaMultiplayerBridge.new(socket)
+ multiplayer_bridge.match_join_error.connect(self._on_match_join_error)
+ multiplayer_bridge.match_joined.connect(self._on_match_joined)
+ get_tree().get_multiplayer().set_multiplayer_peer(multiplayer_bridge.multiplayer_peer)
+
+func _on_match_join_error(error):
+ print ("Unable to join match: ", error.message)
+
+func _on_match_join() -> void:
+ print ("Joined match with id: ", multiplayer_bridge.match_id)
+```
+
+You can also connect to any of the usual signals on `MultiplayerAPI`, for
+example:
+
+```gdscript
+ get_tree().get_multiplayer().peer_connected.connect(self._on_peer_connected)
+ get_tree().get_multiplayer().peer_disconnected.connect(self._on_peer_disconnected)
+
+func _on_peer_connected(peer_id):
+ print ("Peer joined match: ", peer_id)
+
+func _on_peer_disconnected(peer_id):
+ print ("Peer left match: ", peer_id)
+```
+
+Then you need to join a match, using one of the following methods:
+
+- Create a new private match, with your client as the host.
+ ```gdscript
+ multiplayer_bridge.create_match()
+ ```
+
+- Join a private match.
+ ```gdscript
+ multiplayer_bridge.join_match(match_id)
+ ```
+
+- Create or join a private match with the given name.
+ ```gdscript
+ multiplayer_bridge.join_named_match(match_name)
+ ```
+
+- Use the matchmaker to find and join a public match.
+ ```gdscript
+ var ticket = await socket.add_matchmaker_async()
+ if ticket.is_exception():
+ print ("Error joining matchmaking pool: ", ticket.get_exception().message)
+ return
+
+ multiplayer_bridge.start_matchmaking(ticket)
+ ```
+
+After the the "match_joined" signal is emitted, you can start sending RPCs as
+usual with the `rpc()` function, and calling any other functions associated with
+the High-level Multiplayer API, such as `get_tree().get_multiplayer().get_unique_id()`
+and `node.set_network_authority(peer_id)` and `node.is_network_authority()`.
+
+## .NET / C#
+
+If you're using the .NET version of Godot with C# support, you can use the
+[Nakama .NET client](https://github.com/heroiclabs/nakama-dotnet/), which can be
+installed via NuGet:
+
+```
+dotnet add package NakamaClient
+```
+
+This addon includes some C# classes for use with the .NET client, to provide deeper
+integration with Godot:
+
+- `GodotLogger`: A logger which prints to the Godot console.
+- `GodotHttpAdapter`: An HTTP adapter which uses Godot's HTTPRequest node.
+- `GodotWebSocketAdapter`: A socket adapter which uses Godot's WebSocketClient.
+
+Here's an example of how to use them:
+
+```csharp
+ var http_adapter = new GodotHttpAdapter();
+ // It's a Node, so it needs to be added to the scene tree.
+ // Consider putting this in an autoload singleton so it won't go away unexpectedly.
+ AddChild(http_adapter);
+
+ const string scheme = "http";
+ const string host = "127.0.0.1";
+ const int port = 7350;
+ const string serverKey = "defaultkey";
+
+ // Pass in the 'http_adapter' as the last argument.
+ var client = new Client(scheme, host, port, serverKey, http_adapter);
+
+ // To log DEBUG messages to the Godot console.
+ client.Logger = new GodotLogger("Nakama", GodotLogger.LogLevel.DEBUG);
+
+ ISession session;
+ try {
+ session = await client.AuthenticateDeviceAsync(OS.GetUniqueId(), "TestUser", true);
+ }
+ catch (ApiResponseException e) {
+ GD.PrintErr(e.ToString());
+ return;
+ }
+
+ var websocket_adapter = new GodotWebSocketAdapter();
+ // Like the HTTP adapter, it's a Node, so it needs to be added to the scene tree.
+ // Consider putting this in an autoload singleton so it won't go away unexpectedly.
+ AddChild(websocket_adapter);
+
+ // Pass in the 'websocket_adapter' as the last argument.
+ var socket = Socket.From(client, websocket_adapter);
+```
+
+**Note:** _The out-of-the-box Nakama .NET client will work fine with desktop builds of your game! However, it won't work with HTML5 builds, unless you use the `GodotHttpAdapter` and `GodotWebSocketAdapter` classes._
+
+## Contribute
+
+The development roadmap is managed as GitHub issues and pull requests are welcome. If you're interested to improve the code please open an issue to discuss the changes or drop in and discuss it in the [community forum](https://forum.heroiclabs.com).
+
+### Run Tests
+
+To run tests you will need to run the server and database. Most tests are written as integration tests which execute against the server. A quick approach we use with our test workflow is to use the Docker compose file described in the [documentation](https://heroiclabs.com/docs/install-docker-quickstart).
+
+Additionally, you will need to copy (or symlink) the `addons` folder inside the `test_suite` folder. You can now run the `test_suite` project from the Godot Editor.
+
+To run the tests on a headless machine (without a GPU) you can download a copy of [Godot Headless](https://godotengine.org/download/server) and run it from the command line.
+
+To automate this procedure, move the headless binary to `test_suite/bin/godot.elf`, and run the tests via the `test_suite/run_tests.sh` shell script (exit code will report test failure/success).
+
+```shell
+cd nakama
+docker-compose -f ./docker-compose-postgres.yml up
+cd ..
+cd nakama-godot
+sh test_suite/run_tests.sh
+```
+
+### Make a new release
+
+To make a new release ready for distribution, simply zip the addons folder recursively (possibly adding `CHANGELOG`, `LICENSE`, and `README.md` too).
+
+On unix systems, you can run the following command (replacing `$VERSION` with the desired version number). Remember to update the `CHANGELOG` file first.
+
+```shell
+zip -r nakama-$VERSION.zip addons/ LICENSE CHANGELOG.md README.md
+```
+
+### License
+
+This project is licensed under the [Apache-2 License](https://github.com/heroiclabs/nakama-godot/blob/master/LICENSE).
diff --git a/game/addons/com.heroiclabs.nakama/Nakama.gd b/game/addons/com.heroiclabs.nakama/Nakama.gd
new file mode 100644
index 0000000..37781d4
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/Nakama.gd
@@ -0,0 +1,61 @@
+@tool
+extends Node
+
+# The default host address of the server.
+const DEFAULT_HOST : String = "127.0.0.1"
+
+# The default port number of the server.
+const DEFAULT_PORT : int = 7350
+
+# The default timeout for the connections.
+const DEFAULT_TIMEOUT = 3
+
+# The default protocol scheme for the client connection.
+const DEFAULT_CLIENT_SCHEME : String = "http"
+
+# The default protocol scheme for the socket connection.
+const DEFAULT_SOCKET_SCHEME : String = "ws"
+
+# The default log level for the Nakama logger.
+const DEFAULT_LOG_LEVEL = NakamaLogger.LOG_LEVEL.DEBUG
+
+var _http_adapter = null
+var logger = NakamaLogger.new()
+
+func _ready() -> void:
+ process_mode = Node.PROCESS_MODE_ALWAYS
+
+func get_client_adapter() -> NakamaHTTPAdapter:
+ if _http_adapter == null:
+ _http_adapter = NakamaHTTPAdapter.new()
+ _http_adapter.logger = logger
+ _http_adapter.name = "NakamaHTTPAdapter"
+ add_child(_http_adapter)
+ return _http_adapter
+
+func create_socket_adapter() -> NakamaSocketAdapter:
+ var adapter = NakamaSocketAdapter.new()
+ adapter.name = "NakamaWebSocketAdapter"
+ adapter.logger = logger
+ add_child(adapter)
+ return adapter
+
+func create_client(p_server_key : String,
+ p_host : String = DEFAULT_HOST,
+ p_port : int = DEFAULT_PORT,
+ p_scheme : String = DEFAULT_CLIENT_SCHEME,
+ p_timeout : int = DEFAULT_TIMEOUT,
+ p_log_level : int = DEFAULT_LOG_LEVEL) -> NakamaClient:
+ logger._level = p_log_level
+ return NakamaClient.new(get_client_adapter(), p_server_key, p_scheme, p_host, p_port, p_timeout)
+
+func create_socket(p_host : String = DEFAULT_HOST,
+ p_port : int = DEFAULT_PORT,
+ p_scheme : String = DEFAULT_SOCKET_SCHEME) -> NakamaSocket:
+ return NakamaSocket.new(create_socket_adapter(), p_host, p_port, p_scheme, true)
+
+func create_socket_from(p_client : NakamaClient) -> NakamaSocket:
+ var scheme = "ws"
+ if p_client.scheme == "https":
+ scheme = "wss"
+ return NakamaSocket.new(create_socket_adapter(), p_client.host, p_client.port, scheme, true)
diff --git a/game/addons/com.heroiclabs.nakama/Nakama.gd.uid b/game/addons/com.heroiclabs.nakama/Nakama.gd.uid
new file mode 100644
index 0000000..5e8bfb7
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/Nakama.gd.uid
@@ -0,0 +1 @@
+uid://b6tpsu78xwjth
diff --git a/game/addons/com.heroiclabs.nakama/api/NakamaAPI.gd b/game/addons/com.heroiclabs.nakama/api/NakamaAPI.gd
new file mode 100644
index 0000000..fc680bd
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/api/NakamaAPI.gd
@@ -0,0 +1,6120 @@
+### Code generated by codegen/main.go. DO NOT EDIT. ###
+
+extends RefCounted
+class_name NakamaAPI
+
+# A single user-role pair.
+class GroupUserListGroupUser extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "state": {"name": "_state", "type": TYPE_INT, "required": false},
+ "user": {"name": "_user", "type": "ApiUser", "required": false},
+ }
+
+ # Their relationship to the group.
+ var _state
+ var state : int:
+ get:
+ return 0 if not _state is int else int(_state)
+
+ # User.
+ var _user
+ var user : ApiUser:
+ get:
+ return _user as ApiUser
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> GroupUserListGroupUser:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "GroupUserListGroupUser", p_dict), GroupUserListGroupUser) as GroupUserListGroupUser
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "state: %s, " % _state
+ output += "user: %s, " % _user
+ return output
+
+# A single group-role pair.
+class UserGroupListUserGroup extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "group": {"name": "_group", "type": "ApiGroup", "required": false},
+ "state": {"name": "_state", "type": TYPE_INT, "required": false},
+ }
+
+ # Group.
+ var _group
+ var group : ApiGroup:
+ get:
+ return _group as ApiGroup
+
+ # The user's relationship to the group.
+ var _state
+ var state : int:
+ get:
+ return 0 if not _state is int else int(_state)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> UserGroupListUserGroup:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "UserGroupListUserGroup", p_dict), UserGroupListUserGroup) as UserGroupListUserGroup
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "group: %s, " % _group
+ output += "state: %s, " % _state
+ return output
+
+# Record values to write.
+class WriteLeaderboardRecordRequestLeaderboardRecordWrite extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "metadata": {"name": "_metadata", "type": TYPE_STRING, "required": false},
+ "operator": {"name": "_operator", "type": TYPE_INT, "required": false},
+ "score": {"name": "_score", "type": TYPE_STRING, "required": false},
+ "subscore": {"name": "_subscore", "type": TYPE_STRING, "required": false},
+ }
+
+ # Optional record metadata.
+ var _metadata
+ var metadata : String:
+ get:
+ return "" if not _metadata is String else String(_metadata)
+
+ # Operator override.
+ var _operator
+ var operator : int:
+ get:
+ return ApiOperator.values()[0] if not ApiOperator.values().has(_operator) else _operator
+
+ # The score value to submit.
+ var _score
+ var score : String:
+ get:
+ return "" if not _score is String else String(_score)
+
+ # An optional secondary value.
+ var _subscore
+ var subscore : String:
+ get:
+ return "" if not _subscore is String else String(_subscore)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> WriteLeaderboardRecordRequestLeaderboardRecordWrite:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "WriteLeaderboardRecordRequestLeaderboardRecordWrite", p_dict), WriteLeaderboardRecordRequestLeaderboardRecordWrite) as WriteLeaderboardRecordRequestLeaderboardRecordWrite
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "metadata: %s, " % _metadata
+ output += "operator: %s, " % _operator
+ output += "score: %s, " % _score
+ output += "subscore: %s, " % _subscore
+ return output
+
+# Record values to write.
+class WriteTournamentRecordRequestTournamentRecordWrite extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "metadata": {"name": "_metadata", "type": TYPE_STRING, "required": false},
+ "operator": {"name": "_operator", "type": TYPE_INT, "required": false},
+ "score": {"name": "_score", "type": TYPE_STRING, "required": false},
+ "subscore": {"name": "_subscore", "type": TYPE_STRING, "required": false},
+ }
+
+ # A JSON object of additional properties (optional).
+ var _metadata
+ var metadata : String:
+ get:
+ return "" if not _metadata is String else String(_metadata)
+
+ # Operator override.
+ var _operator
+ var operator : int:
+ get:
+ return ApiOperator.values()[0] if not ApiOperator.values().has(_operator) else _operator
+
+ # The score value to submit.
+ var _score
+ var score : String:
+ get:
+ return "" if not _score is String else String(_score)
+
+ # An optional secondary value.
+ var _subscore
+ var subscore : String:
+ get:
+ return "" if not _subscore is String else String(_subscore)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> WriteTournamentRecordRequestTournamentRecordWrite:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "WriteTournamentRecordRequestTournamentRecordWrite", p_dict), WriteTournamentRecordRequestTournamentRecordWrite) as WriteTournamentRecordRequestTournamentRecordWrite
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "metadata: %s, " % _metadata
+ output += "operator: %s, " % _operator
+ output += "score: %s, " % _score
+ output += "subscore: %s, " % _subscore
+ return output
+
+# A user with additional account details. Always the current user.
+class ApiAccount extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "custom_id": {"name": "_custom_id", "type": TYPE_STRING, "required": false},
+ "devices": {"name": "_devices", "type": TYPE_ARRAY, "required": false, "content": "ApiAccountDevice"},
+ "disable_time": {"name": "_disable_time", "type": TYPE_STRING, "required": false},
+ "email": {"name": "_email", "type": TYPE_STRING, "required": false},
+ "user": {"name": "_user", "type": "ApiUser", "required": false},
+ "verify_time": {"name": "_verify_time", "type": TYPE_STRING, "required": false},
+ "wallet": {"name": "_wallet", "type": TYPE_STRING, "required": false},
+ }
+
+ # The custom id in the user's account.
+ var _custom_id
+ var custom_id : String:
+ get:
+ return "" if not _custom_id is String else String(_custom_id)
+
+ # The devices which belong to the user's account.
+ var _devices
+ var devices : Array:
+ get:
+ return Array() if not _devices is Array else Array(_devices)
+
+ # The UNIX time when the user's account was disabled/banned.
+ var _disable_time
+ var disable_time : String:
+ get:
+ return "" if not _disable_time is String else String(_disable_time)
+
+ # The email address of the user.
+ var _email
+ var email : String:
+ get:
+ return "" if not _email is String else String(_email)
+
+ # The user object.
+ var _user
+ var user : ApiUser:
+ get:
+ return _user as ApiUser
+
+ # The UNIX time when the user's email was verified.
+ var _verify_time
+ var verify_time : String:
+ get:
+ return "" if not _verify_time is String else String(_verify_time)
+
+ # The user's wallet data.
+ var _wallet
+ var wallet : String:
+ get:
+ return "" if not _wallet is String else String(_wallet)
+
+ var _wallet_dict = null
+ var wallet_dict : Dictionary:
+ get:
+ if _wallet_dict == null:
+ if _wallet == null:
+ return {}
+ var json = JSON.new()
+ if json.parse(_wallet) != OK:
+ return {}
+ _wallet_dict = json.get_data()
+ return _wallet_dict as Dictionary
+
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccount:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccount", p_dict), ApiAccount) as ApiAccount
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "custom_id: %s, " % _custom_id
+ output += "devices: %s, " % [_devices]
+ output += "disable_time: %s, " % _disable_time
+ output += "email: %s, " % _email
+ output += "user: %s, " % _user
+ output += "verify_time: %s, " % _verify_time
+ output += "wallet: %s, " % _wallet
+ return output
+
+# Send a Apple Sign In token to the server. Used with authenticate/link/unlink.
+class ApiAccountApple extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "token": {"name": "_token", "type": TYPE_STRING, "required": false},
+ "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ }
+
+ # The ID token received from Apple to validate.
+ var _token
+ var token : String:
+ get:
+ return "" if not _token is String else String(_token)
+
+ # Extra information that will be bundled in the session token.
+ var _vars
+ var vars : Dictionary:
+ get:
+ return Dictionary() if not _vars is Dictionary else _vars.duplicate()
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountApple:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountApple", p_dict), ApiAccountApple) as ApiAccountApple
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "token: %s, " % _token
+ var map_string : String = ""
+ if typeof(_vars) == TYPE_DICTIONARY:
+ for k in _vars:
+ map_string += "{%s=%s}, " % [k, _vars[k]]
+ output += "vars: [%s], " % map_string
+ return output
+
+# Send a custom ID to the server. Used with authenticate/link/unlink.
+class ApiAccountCustom extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "id": {"name": "_id", "type": TYPE_STRING, "required": false},
+ "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ }
+
+ # A custom identifier.
+ var _id
+ var id : String:
+ get:
+ return "" if not _id is String else String(_id)
+
+ # Extra information that will be bundled in the session token.
+ var _vars
+ var vars : Dictionary:
+ get:
+ return Dictionary() if not _vars is Dictionary else _vars.duplicate()
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountCustom:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountCustom", p_dict), ApiAccountCustom) as ApiAccountCustom
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "id: %s, " % _id
+ var map_string : String = ""
+ if typeof(_vars) == TYPE_DICTIONARY:
+ for k in _vars:
+ map_string += "{%s=%s}, " % [k, _vars[k]]
+ output += "vars: [%s], " % map_string
+ return output
+
+# Send a device to the server. Used with authenticate/link/unlink and user.
+class ApiAccountDevice extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "id": {"name": "_id", "type": TYPE_STRING, "required": false},
+ "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ }
+
+ # A device identifier. Should be obtained by a platform-specific device API.
+ var _id
+ var id : String:
+ get:
+ return "" if not _id is String else String(_id)
+
+ # Extra information that will be bundled in the session token.
+ var _vars
+ var vars : Dictionary:
+ get:
+ return Dictionary() if not _vars is Dictionary else _vars.duplicate()
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountDevice:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountDevice", p_dict), ApiAccountDevice) as ApiAccountDevice
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "id: %s, " % _id
+ var map_string : String = ""
+ if typeof(_vars) == TYPE_DICTIONARY:
+ for k in _vars:
+ map_string += "{%s=%s}, " % [k, _vars[k]]
+ output += "vars: [%s], " % map_string
+ return output
+
+# Send an email with password to the server. Used with authenticate/link/unlink.
+class ApiAccountEmail extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "email": {"name": "_email", "type": TYPE_STRING, "required": false},
+ "password": {"name": "_password", "type": TYPE_STRING, "required": false},
+ "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ }
+
+ # A valid RFC-5322 email address.
+ var _email
+ var email : String:
+ get:
+ return "" if not _email is String else String(_email)
+
+ # A password for the user account.
+ var _password
+ var password : String:
+ get:
+ return "" if not _password is String else String(_password)
+
+ # Extra information that will be bundled in the session token.
+ var _vars
+ var vars : Dictionary:
+ get:
+ return Dictionary() if not _vars is Dictionary else _vars.duplicate()
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountEmail:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountEmail", p_dict), ApiAccountEmail) as ApiAccountEmail
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "email: %s, " % _email
+ output += "password: %s, " % _password
+ var map_string : String = ""
+ if typeof(_vars) == TYPE_DICTIONARY:
+ for k in _vars:
+ map_string += "{%s=%s}, " % [k, _vars[k]]
+ output += "vars: [%s], " % map_string
+ return output
+
+# Send a Facebook token to the server. Used with authenticate/link/unlink.
+class ApiAccountFacebook extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "token": {"name": "_token", "type": TYPE_STRING, "required": false},
+ "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ }
+
+ # The OAuth token received from Facebook to access their profile API.
+ var _token
+ var token : String:
+ get:
+ return "" if not _token is String else String(_token)
+
+ # Extra information that will be bundled in the session token.
+ var _vars
+ var vars : Dictionary:
+ get:
+ return Dictionary() if not _vars is Dictionary else _vars.duplicate()
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountFacebook:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountFacebook", p_dict), ApiAccountFacebook) as ApiAccountFacebook
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "token: %s, " % _token
+ var map_string : String = ""
+ if typeof(_vars) == TYPE_DICTIONARY:
+ for k in _vars:
+ map_string += "{%s=%s}, " % [k, _vars[k]]
+ output += "vars: [%s], " % map_string
+ return output
+
+# Send a Facebook Instant Game token to the server. Used with authenticate/link/unlink.
+class ApiAccountFacebookInstantGame extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "signed_player_info": {"name": "_signed_player_info", "type": TYPE_STRING, "required": false},
+ "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ }
+
+ #
+ var _signed_player_info
+ var signed_player_info : String:
+ get:
+ return "" if not _signed_player_info is String else String(_signed_player_info)
+
+ # Extra information that will be bundled in the session token.
+ var _vars
+ var vars : Dictionary:
+ get:
+ return Dictionary() if not _vars is Dictionary else _vars.duplicate()
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountFacebookInstantGame:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountFacebookInstantGame", p_dict), ApiAccountFacebookInstantGame) as ApiAccountFacebookInstantGame
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "signed_player_info: %s, " % _signed_player_info
+ var map_string : String = ""
+ if typeof(_vars) == TYPE_DICTIONARY:
+ for k in _vars:
+ map_string += "{%s=%s}, " % [k, _vars[k]]
+ output += "vars: [%s], " % map_string
+ return output
+
+# Send Apple's Game Center account credentials to the server. Used with authenticate/link/unlink.
+class ApiAccountGameCenter extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "bundle_id": {"name": "_bundle_id", "type": TYPE_STRING, "required": false},
+ "player_id": {"name": "_player_id", "type": TYPE_STRING, "required": false},
+ "public_key_url": {"name": "_public_key_url", "type": TYPE_STRING, "required": false},
+ "salt": {"name": "_salt", "type": TYPE_STRING, "required": false},
+ "signature": {"name": "_signature", "type": TYPE_STRING, "required": false},
+ "timestamp_seconds": {"name": "_timestamp_seconds", "type": TYPE_STRING, "required": false},
+ "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ }
+
+ # Bundle ID (generated by GameCenter).
+ var _bundle_id
+ var bundle_id : String:
+ get:
+ return "" if not _bundle_id is String else String(_bundle_id)
+
+ # Player ID (generated by GameCenter).
+ var _player_id
+ var player_id : String:
+ get:
+ return "" if not _player_id is String else String(_player_id)
+
+ # The URL for the public encryption key.
+ var _public_key_url
+ var public_key_url : String:
+ get:
+ return "" if not _public_key_url is String else String(_public_key_url)
+
+ # A random "NSString" used to compute the hash and keep it randomized.
+ var _salt
+ var salt : String:
+ get:
+ return "" if not _salt is String else String(_salt)
+
+ # The verification signature data generated.
+ var _signature
+ var signature : String:
+ get:
+ return "" if not _signature is String else String(_signature)
+
+ # Time since UNIX epoch when the signature was created.
+ var _timestamp_seconds
+ var timestamp_seconds : String:
+ get:
+ return "" if not _timestamp_seconds is String else String(_timestamp_seconds)
+
+ # Extra information that will be bundled in the session token.
+ var _vars
+ var vars : Dictionary:
+ get:
+ return Dictionary() if not _vars is Dictionary else _vars.duplicate()
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountGameCenter:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountGameCenter", p_dict), ApiAccountGameCenter) as ApiAccountGameCenter
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "bundle_id: %s, " % _bundle_id
+ output += "player_id: %s, " % _player_id
+ output += "public_key_url: %s, " % _public_key_url
+ output += "salt: %s, " % _salt
+ output += "signature: %s, " % _signature
+ output += "timestamp_seconds: %s, " % _timestamp_seconds
+ var map_string : String = ""
+ if typeof(_vars) == TYPE_DICTIONARY:
+ for k in _vars:
+ map_string += "{%s=%s}, " % [k, _vars[k]]
+ output += "vars: [%s], " % map_string
+ return output
+
+# Send a Google token to the server. Used with authenticate/link/unlink.
+class ApiAccountGoogle extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "token": {"name": "_token", "type": TYPE_STRING, "required": false},
+ "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ }
+
+ # The OAuth token received from Google to access their profile API.
+ var _token
+ var token : String:
+ get:
+ return "" if not _token is String else String(_token)
+
+ # Extra information that will be bundled in the session token.
+ var _vars
+ var vars : Dictionary:
+ get:
+ return Dictionary() if not _vars is Dictionary else _vars.duplicate()
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountGoogle:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountGoogle", p_dict), ApiAccountGoogle) as ApiAccountGoogle
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "token: %s, " % _token
+ var map_string : String = ""
+ if typeof(_vars) == TYPE_DICTIONARY:
+ for k in _vars:
+ map_string += "{%s=%s}, " % [k, _vars[k]]
+ output += "vars: [%s], " % map_string
+ return output
+
+# Send a Steam token to the server. Used with authenticate/link/unlink.
+class ApiAccountSteam extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "token": {"name": "_token", "type": TYPE_STRING, "required": false},
+ "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ }
+
+ # The account token received from Steam to access their profile API.
+ var _token
+ var token : String:
+ get:
+ return "" if not _token is String else String(_token)
+
+ # Extra information that will be bundled in the session token.
+ var _vars
+ var vars : Dictionary:
+ get:
+ return Dictionary() if not _vars is Dictionary else _vars.duplicate()
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiAccountSteam:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiAccountSteam", p_dict), ApiAccountSteam) as ApiAccountSteam
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "token: %s, " % _token
+ var map_string : String = ""
+ if typeof(_vars) == TYPE_DICTIONARY:
+ for k in _vars:
+ map_string += "{%s=%s}, " % [k, _vars[k]]
+ output += "vars: [%s], " % map_string
+ return output
+
+# A message sent on a channel.
+class ApiChannelMessage extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "channel_id": {"name": "_channel_id", "type": TYPE_STRING, "required": false},
+ "code": {"name": "_code", "type": TYPE_INT, "required": false},
+ "content": {"name": "_content", "type": TYPE_STRING, "required": false},
+ "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false},
+ "group_id": {"name": "_group_id", "type": TYPE_STRING, "required": false},
+ "message_id": {"name": "_message_id", "type": TYPE_STRING, "required": false},
+ "persistent": {"name": "_persistent", "type": TYPE_BOOL, "required": false},
+ "room_name": {"name": "_room_name", "type": TYPE_STRING, "required": false},
+ "sender_id": {"name": "_sender_id", "type": TYPE_STRING, "required": false},
+ "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false},
+ "user_id_one": {"name": "_user_id_one", "type": TYPE_STRING, "required": false},
+ "user_id_two": {"name": "_user_id_two", "type": TYPE_STRING, "required": false},
+ "username": {"name": "_username", "type": TYPE_STRING, "required": false},
+ }
+
+ # The channel this message belongs to.
+ var _channel_id
+ var channel_id : String:
+ get:
+ return "" if not _channel_id is String else String(_channel_id)
+
+ # The code representing a message type or category.
+ var _code
+ var code : int:
+ get:
+ return 0 if not _code is int else int(_code)
+
+ # The content payload.
+ var _content
+ var content : String:
+ get:
+ return "" if not _content is String else String(_content)
+
+ # The UNIX time when the message was created.
+ var _create_time
+ var create_time : String:
+ get:
+ return "" if not _create_time is String else String(_create_time)
+
+ # The ID of the group, or an empty string if this message was not sent through a group channel.
+ var _group_id
+ var group_id : String:
+ get:
+ return "" if not _group_id is String else String(_group_id)
+
+ # The unique ID of this message.
+ var _message_id
+ var message_id : String:
+ get:
+ return "" if not _message_id is String else String(_message_id)
+
+ # True if the message was persisted to the channel's history, false otherwise.
+ var _persistent
+ var persistent : bool:
+ get:
+ return false if not _persistent is bool else bool(_persistent)
+
+ # The name of the chat room, or an empty string if this message was not sent through a chat room.
+ var _room_name
+ var room_name : String:
+ get:
+ return "" if not _room_name is String else String(_room_name)
+
+ # Message sender, usually a user ID.
+ var _sender_id
+ var sender_id : String:
+ get:
+ return "" if not _sender_id is String else String(_sender_id)
+
+ # The UNIX time when the message was last updated.
+ var _update_time
+ var update_time : String:
+ get:
+ return "" if not _update_time is String else String(_update_time)
+
+ # The ID of the first DM user, or an empty string if this message was not sent through a DM chat.
+ var _user_id_one
+ var user_id_one : String:
+ get:
+ return "" if not _user_id_one is String else String(_user_id_one)
+
+ # The ID of the second DM user, or an empty string if this message was not sent through a DM chat.
+ var _user_id_two
+ var user_id_two : String:
+ get:
+ return "" if not _user_id_two is String else String(_user_id_two)
+
+ # The username of the message sender, if any.
+ var _username
+ var username : String:
+ get:
+ return "" if not _username is String else String(_username)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiChannelMessage:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiChannelMessage", p_dict), ApiChannelMessage) as ApiChannelMessage
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "channel_id: %s, " % _channel_id
+ output += "code: %s, " % _code
+ output += "content: %s, " % _content
+ output += "create_time: %s, " % _create_time
+ output += "group_id: %s, " % _group_id
+ output += "message_id: %s, " % _message_id
+ output += "persistent: %s, " % _persistent
+ output += "room_name: %s, " % _room_name
+ output += "sender_id: %s, " % _sender_id
+ output += "update_time: %s, " % _update_time
+ output += "user_id_one: %s, " % _user_id_one
+ output += "user_id_two: %s, " % _user_id_two
+ output += "username: %s, " % _username
+ return output
+
+# A list of channel messages, usually a result of a list operation.
+class ApiChannelMessageList extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "cacheable_cursor": {"name": "_cacheable_cursor", "type": TYPE_STRING, "required": false},
+ "messages": {"name": "_messages", "type": TYPE_ARRAY, "required": false, "content": "ApiChannelMessage"},
+ "next_cursor": {"name": "_next_cursor", "type": TYPE_STRING, "required": false},
+ "prev_cursor": {"name": "_prev_cursor", "type": TYPE_STRING, "required": false},
+ }
+
+ # Cacheable cursor to list newer messages. Durable and designed to be stored, unlike next/prev cursors.
+ var _cacheable_cursor
+ var cacheable_cursor : String:
+ get:
+ return "" if not _cacheable_cursor is String else String(_cacheable_cursor)
+
+ # A list of messages.
+ var _messages
+ var messages : Array:
+ get:
+ return Array() if not _messages is Array else Array(_messages)
+
+ # The cursor to send when retrieving the next page, if any.
+ var _next_cursor
+ var next_cursor : String:
+ get:
+ return "" if not _next_cursor is String else String(_next_cursor)
+
+ # The cursor to send when retrieving the previous page, if any.
+ var _prev_cursor
+ var prev_cursor : String:
+ get:
+ return "" if not _prev_cursor is String else String(_prev_cursor)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiChannelMessageList:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiChannelMessageList", p_dict), ApiChannelMessageList) as ApiChannelMessageList
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "cacheable_cursor: %s, " % _cacheable_cursor
+ output += "messages: %s, " % [_messages]
+ output += "next_cursor: %s, " % _next_cursor
+ output += "prev_cursor: %s, " % _prev_cursor
+ return output
+
+# Create a group with the current user as owner.
+class ApiCreateGroupRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "avatar_url": {"name": "_avatar_url", "type": TYPE_STRING, "required": false},
+ "description": {"name": "_description", "type": TYPE_STRING, "required": false},
+ "lang_tag": {"name": "_lang_tag", "type": TYPE_STRING, "required": false},
+ "max_count": {"name": "_max_count", "type": TYPE_INT, "required": false},
+ "name": {"name": "_name", "type": TYPE_STRING, "required": false},
+ "open": {"name": "_open", "type": TYPE_BOOL, "required": false},
+ }
+
+ # A URL for an avatar image.
+ var _avatar_url
+ var avatar_url : String:
+ get:
+ return "" if not _avatar_url is String else String(_avatar_url)
+
+ # A description for the group.
+ var _description
+ var description : String:
+ get:
+ return "" if not _description is String else String(_description)
+
+ # The language expected to be a tag which follows the BCP-47 spec.
+ var _lang_tag
+ var lang_tag : String:
+ get:
+ return "" if not _lang_tag is String else String(_lang_tag)
+
+ # Maximum number of group members.
+ var _max_count
+ var max_count : int:
+ get:
+ return 0 if not _max_count is int else int(_max_count)
+
+ # A unique name for the group.
+ var _name
+ var name : String:
+ get:
+ return "" if not _name is String else String(_name)
+
+ # Mark a group as open or not where only admins can accept members.
+ var _open
+ var open : bool:
+ get:
+ return false if not _open is bool else bool(_open)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiCreateGroupRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiCreateGroupRequest", p_dict), ApiCreateGroupRequest) as ApiCreateGroupRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "avatar_url: %s, " % _avatar_url
+ output += "description: %s, " % _description
+ output += "lang_tag: %s, " % _lang_tag
+ output += "max_count: %s, " % _max_count
+ output += "name: %s, " % _name
+ output += "open: %s, " % _open
+ return output
+
+# Storage objects to delete.
+class ApiDeleteStorageObjectId extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "collection": {"name": "_collection", "type": TYPE_STRING, "required": false},
+ "key": {"name": "_key", "type": TYPE_STRING, "required": false},
+ "version": {"name": "_version", "type": TYPE_STRING, "required": false},
+ }
+
+ # The collection which stores the object.
+ var _collection
+ var collection : String:
+ get:
+ return "" if not _collection is String else String(_collection)
+
+ # The key of the object within the collection.
+ var _key
+ var key : String:
+ get:
+ return "" if not _key is String else String(_key)
+
+ # The version hash of the object.
+ var _version
+ var version : String:
+ get:
+ return "" if not _version is String else String(_version)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiDeleteStorageObjectId:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiDeleteStorageObjectId", p_dict), ApiDeleteStorageObjectId) as ApiDeleteStorageObjectId
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "collection: %s, " % _collection
+ output += "key: %s, " % _key
+ output += "version: %s, " % _version
+ return output
+
+# Batch delete storage objects.
+class ApiDeleteStorageObjectsRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "object_ids": {"name": "_object_ids", "type": TYPE_ARRAY, "required": false, "content": "ApiDeleteStorageObjectId"},
+ }
+
+ # Batch of storage objects.
+ var _object_ids
+ var object_ids : Array:
+ get:
+ return Array() if not _object_ids is Array else Array(_object_ids)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiDeleteStorageObjectsRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiDeleteStorageObjectsRequest", p_dict), ApiDeleteStorageObjectsRequest) as ApiDeleteStorageObjectsRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "object_ids: %s, " % [_object_ids]
+ return output
+
+# Represents an event to be passed through the server to registered event handlers.
+class ApiEvent extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "external": {"name": "_external", "type": TYPE_BOOL, "required": false},
+ "name": {"name": "_name", "type": TYPE_STRING, "required": false},
+ "properties": {"name": "_properties", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ "timestamp": {"name": "_timestamp", "type": TYPE_STRING, "required": false},
+ }
+
+ # True if the event came directly from a client call, false otherwise.
+ var _external
+ var external : bool:
+ get:
+ return false if not _external is bool else bool(_external)
+
+ # An event name, type, category, or identifier.
+ var _name
+ var name : String:
+ get:
+ return "" if not _name is String else String(_name)
+
+ # Arbitrary event property values.
+ var _properties
+ var properties : Dictionary:
+ get:
+ return Dictionary() if not _properties is Dictionary else _properties.duplicate()
+
+ # The time when the event was triggered.
+ var _timestamp
+ var timestamp : String:
+ get:
+ return "" if not _timestamp is String else String(_timestamp)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiEvent:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiEvent", p_dict), ApiEvent) as ApiEvent
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "external: %s, " % _external
+ output += "name: %s, " % _name
+ var map_string : String = ""
+ if typeof(_properties) == TYPE_DICTIONARY:
+ for k in _properties:
+ map_string += "{%s=%s}, " % [k, _properties[k]]
+ output += "properties: [%s], " % map_string
+ output += "timestamp: %s, " % _timestamp
+ return output
+
+# A friend of a user.
+class ApiFriend extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "state": {"name": "_state", "type": TYPE_INT, "required": false},
+ "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false},
+ "user": {"name": "_user", "type": "ApiUser", "required": false},
+ }
+
+ # The friend status.
+ var _state
+ var state : int:
+ get:
+ return 0 if not _state is int else int(_state)
+
+ # Time of the latest relationship update.
+ var _update_time
+ var update_time : String:
+ get:
+ return "" if not _update_time is String else String(_update_time)
+
+ # The user object.
+ var _user
+ var user : ApiUser:
+ get:
+ return _user as ApiUser
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiFriend:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiFriend", p_dict), ApiFriend) as ApiFriend
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "state: %s, " % _state
+ output += "update_time: %s, " % _update_time
+ output += "user: %s, " % _user
+ return output
+
+# A collection of zero or more friends of the user.
+class ApiFriendList extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false},
+ "friends": {"name": "_friends", "type": TYPE_ARRAY, "required": false, "content": "ApiFriend"},
+ }
+
+ # Cursor for the next page of results, if any.
+ var _cursor
+ var cursor : String:
+ get:
+ return "" if not _cursor is String else String(_cursor)
+
+ # The Friend objects.
+ var _friends
+ var friends : Array:
+ get:
+ return Array() if not _friends is Array else Array(_friends)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiFriendList:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiFriendList", p_dict), ApiFriendList) as ApiFriendList
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "cursor: %s, " % _cursor
+ output += "friends: %s, " % [_friends]
+ return output
+
+# A group in the server.
+class ApiGroup extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "avatar_url": {"name": "_avatar_url", "type": TYPE_STRING, "required": false},
+ "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false},
+ "creator_id": {"name": "_creator_id", "type": TYPE_STRING, "required": false},
+ "description": {"name": "_description", "type": TYPE_STRING, "required": false},
+ "edge_count": {"name": "_edge_count", "type": TYPE_INT, "required": false},
+ "id": {"name": "_id", "type": TYPE_STRING, "required": false},
+ "lang_tag": {"name": "_lang_tag", "type": TYPE_STRING, "required": false},
+ "max_count": {"name": "_max_count", "type": TYPE_INT, "required": false},
+ "metadata": {"name": "_metadata", "type": TYPE_STRING, "required": false},
+ "name": {"name": "_name", "type": TYPE_STRING, "required": false},
+ "open": {"name": "_open", "type": TYPE_BOOL, "required": false},
+ "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false},
+ }
+
+ # A URL for an avatar image.
+ var _avatar_url
+ var avatar_url : String:
+ get:
+ return "" if not _avatar_url is String else String(_avatar_url)
+
+ # The UNIX time when the group was created.
+ var _create_time
+ var create_time : String:
+ get:
+ return "" if not _create_time is String else String(_create_time)
+
+ # The id of the user who created the group.
+ var _creator_id
+ var creator_id : String:
+ get:
+ return "" if not _creator_id is String else String(_creator_id)
+
+ # A description for the group.
+ var _description
+ var description : String:
+ get:
+ return "" if not _description is String else String(_description)
+
+ # The current count of all members in the group.
+ var _edge_count
+ var edge_count : int:
+ get:
+ return 0 if not _edge_count is int else int(_edge_count)
+
+ # The id of a group.
+ var _id
+ var id : String:
+ get:
+ return "" if not _id is String else String(_id)
+
+ # The language expected to be a tag which follows the BCP-47 spec.
+ var _lang_tag
+ var lang_tag : String:
+ get:
+ return "" if not _lang_tag is String else String(_lang_tag)
+
+ # The maximum number of members allowed.
+ var _max_count
+ var max_count : int:
+ get:
+ return 0 if not _max_count is int else int(_max_count)
+
+ # Additional information stored as a JSON object.
+ var _metadata
+ var metadata : String:
+ get:
+ return "" if not _metadata is String else String(_metadata)
+
+ # The unique name of the group.
+ var _name
+ var name : String:
+ get:
+ return "" if not _name is String else String(_name)
+
+ # Anyone can join open groups, otherwise only admins can accept members.
+ var _open
+ var open : bool:
+ get:
+ return false if not _open is bool else bool(_open)
+
+ # The UNIX time when the group was last updated.
+ var _update_time
+ var update_time : String:
+ get:
+ return "" if not _update_time is String else String(_update_time)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiGroup:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiGroup", p_dict), ApiGroup) as ApiGroup
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "avatar_url: %s, " % _avatar_url
+ output += "create_time: %s, " % _create_time
+ output += "creator_id: %s, " % _creator_id
+ output += "description: %s, " % _description
+ output += "edge_count: %s, " % _edge_count
+ output += "id: %s, " % _id
+ output += "lang_tag: %s, " % _lang_tag
+ output += "max_count: %s, " % _max_count
+ output += "metadata: %s, " % _metadata
+ output += "name: %s, " % _name
+ output += "open: %s, " % _open
+ output += "update_time: %s, " % _update_time
+ return output
+
+# One or more groups returned from a listing operation.
+class ApiGroupList extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false},
+ "groups": {"name": "_groups", "type": TYPE_ARRAY, "required": false, "content": "ApiGroup"},
+ }
+
+ # A cursor used to get the next page.
+ var _cursor
+ var cursor : String:
+ get:
+ return "" if not _cursor is String else String(_cursor)
+
+ # One or more groups.
+ var _groups
+ var groups : Array:
+ get:
+ return Array() if not _groups is Array else Array(_groups)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiGroupList:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiGroupList", p_dict), ApiGroupList) as ApiGroupList
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "cursor: %s, " % _cursor
+ output += "groups: %s, " % [_groups]
+ return output
+
+# A list of users belonging to a group, along with their role.
+class ApiGroupUserList extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false},
+ "group_users": {"name": "_group_users", "type": TYPE_ARRAY, "required": false, "content": "GroupUserListGroupUser"},
+ }
+
+ # Cursor for the next page of results, if any.
+ var _cursor
+ var cursor : String:
+ get:
+ return "" if not _cursor is String else String(_cursor)
+
+ # User-role pairs for a group.
+ var _group_users
+ var group_users : Array:
+ get:
+ return Array() if not _group_users is Array else Array(_group_users)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiGroupUserList:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiGroupUserList", p_dict), ApiGroupUserList) as ApiGroupUserList
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "cursor: %s, " % _cursor
+ output += "group_users: %s, " % [_group_users]
+ return output
+
+# Represents a complete leaderboard record with all scores and associated metadata.
+class ApiLeaderboardRecord extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false},
+ "expiry_time": {"name": "_expiry_time", "type": TYPE_STRING, "required": false},
+ "leaderboard_id": {"name": "_leaderboard_id", "type": TYPE_STRING, "required": false},
+ "max_num_score": {"name": "_max_num_score", "type": TYPE_INT, "required": false},
+ "metadata": {"name": "_metadata", "type": TYPE_STRING, "required": false},
+ "num_score": {"name": "_num_score", "type": TYPE_INT, "required": false},
+ "owner_id": {"name": "_owner_id", "type": TYPE_STRING, "required": false},
+ "rank": {"name": "_rank", "type": TYPE_STRING, "required": false},
+ "score": {"name": "_score", "type": TYPE_STRING, "required": false},
+ "subscore": {"name": "_subscore", "type": TYPE_STRING, "required": false},
+ "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false},
+ "username": {"name": "_username", "type": TYPE_STRING, "required": false},
+ }
+
+ # The UNIX time when the leaderboard record was created.
+ var _create_time
+ var create_time : String:
+ get:
+ return "" if not _create_time is String else String(_create_time)
+
+ # The UNIX time when the leaderboard record expires.
+ var _expiry_time
+ var expiry_time : String:
+ get:
+ return "" if not _expiry_time is String else String(_expiry_time)
+
+ # The ID of the leaderboard this score belongs to.
+ var _leaderboard_id
+ var leaderboard_id : String:
+ get:
+ return "" if not _leaderboard_id is String else String(_leaderboard_id)
+
+ # The maximum number of score updates allowed by the owner.
+ var _max_num_score
+ var max_num_score : int:
+ get:
+ return 0 if not _max_num_score is int else int(_max_num_score)
+
+ # Metadata.
+ var _metadata
+ var metadata : String:
+ get:
+ return "" if not _metadata is String else String(_metadata)
+
+ # The number of submissions to this score record.
+ var _num_score
+ var num_score : int:
+ get:
+ return 0 if not _num_score is int else int(_num_score)
+
+ # The ID of the score owner, usually a user or group.
+ var _owner_id
+ var owner_id : String:
+ get:
+ return "" if not _owner_id is String else String(_owner_id)
+
+ # The rank of this record.
+ var _rank
+ var rank : String:
+ get:
+ return "" if not _rank is String else String(_rank)
+
+ # The score value.
+ var _score
+ var score : String:
+ get:
+ return "" if not _score is String else String(_score)
+
+ # An optional subscore value.
+ var _subscore
+ var subscore : String:
+ get:
+ return "" if not _subscore is String else String(_subscore)
+
+ # The UNIX time when the leaderboard record was updated.
+ var _update_time
+ var update_time : String:
+ get:
+ return "" if not _update_time is String else String(_update_time)
+
+ # The username of the score owner, if the owner is a user.
+ var _username
+ var username : String:
+ get:
+ return "" if not _username is String else String(_username)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiLeaderboardRecord:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiLeaderboardRecord", p_dict), ApiLeaderboardRecord) as ApiLeaderboardRecord
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "create_time: %s, " % _create_time
+ output += "expiry_time: %s, " % _expiry_time
+ output += "leaderboard_id: %s, " % _leaderboard_id
+ output += "max_num_score: %s, " % _max_num_score
+ output += "metadata: %s, " % _metadata
+ output += "num_score: %s, " % _num_score
+ output += "owner_id: %s, " % _owner_id
+ output += "rank: %s, " % _rank
+ output += "score: %s, " % _score
+ output += "subscore: %s, " % _subscore
+ output += "update_time: %s, " % _update_time
+ output += "username: %s, " % _username
+ return output
+
+# A set of leaderboard records, may be part of a leaderboard records page or a batch of individual records.
+class ApiLeaderboardRecordList extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "next_cursor": {"name": "_next_cursor", "type": TYPE_STRING, "required": false},
+ "owner_records": {"name": "_owner_records", "type": TYPE_ARRAY, "required": false, "content": "ApiLeaderboardRecord"},
+ "prev_cursor": {"name": "_prev_cursor", "type": TYPE_STRING, "required": false},
+ "records": {"name": "_records", "type": TYPE_ARRAY, "required": false, "content": "ApiLeaderboardRecord"},
+ }
+
+ # The cursor to send when retrieving the next page, if any.
+ var _next_cursor
+ var next_cursor : String:
+ get:
+ return "" if not _next_cursor is String else String(_next_cursor)
+
+ # A batched set of leaderboard records belonging to specified owners.
+ var _owner_records
+ var owner_records : Array:
+ get:
+ return Array() if not _owner_records is Array else Array(_owner_records)
+
+ # The cursor to send when retrieving the previous page, if any.
+ var _prev_cursor
+ var prev_cursor : String:
+ get:
+ return "" if not _prev_cursor is String else String(_prev_cursor)
+
+ # A list of leaderboard records.
+ var _records
+ var records : Array:
+ get:
+ return Array() if not _records is Array else Array(_records)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiLeaderboardRecordList:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiLeaderboardRecordList", p_dict), ApiLeaderboardRecordList) as ApiLeaderboardRecordList
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "next_cursor: %s, " % _next_cursor
+ output += "owner_records: %s, " % [_owner_records]
+ output += "prev_cursor: %s, " % _prev_cursor
+ output += "records: %s, " % [_records]
+ return output
+
+# Link Steam to the current user's account.
+class ApiLinkSteamRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "account": {"name": "_account", "type": "ApiAccountSteam", "required": false},
+ "sync": {"name": "_sync", "type": TYPE_BOOL, "required": false},
+ }
+
+ # The Facebook account details.
+ var _account
+ var account : ApiAccountSteam:
+ get:
+ return _account as ApiAccountSteam
+
+ # Import Steam friends for the user.
+ var _sync
+ var sync : bool:
+ get:
+ return false if not _sync is bool else bool(_sync)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiLinkSteamRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiLinkSteamRequest", p_dict), ApiLinkSteamRequest) as ApiLinkSteamRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "account: %s, " % _account
+ output += "sync: %s, " % _sync
+ return output
+
+# List user subscriptions.
+class ApiListSubscriptionsRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false},
+ "limit": {"name": "_limit", "type": TYPE_INT, "required": false},
+ }
+
+ #
+ var _cursor
+ var cursor : String:
+ get:
+ return "" if not _cursor is String else String(_cursor)
+
+ #
+ var _limit
+ var limit : int:
+ get:
+ return 0 if not _limit is int else int(_limit)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiListSubscriptionsRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiListSubscriptionsRequest", p_dict), ApiListSubscriptionsRequest) as ApiListSubscriptionsRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "cursor: %s, " % _cursor
+ output += "limit: %s, " % _limit
+ return output
+
+# Represents a realtime match.
+class ApiMatch extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "authoritative": {"name": "_authoritative", "type": TYPE_BOOL, "required": false},
+ "handler_name": {"name": "_handler_name", "type": TYPE_STRING, "required": false},
+ "label": {"name": "_label", "type": TYPE_STRING, "required": false},
+ "match_id": {"name": "_match_id", "type": TYPE_STRING, "required": false},
+ "size": {"name": "_size", "type": TYPE_INT, "required": false},
+ "tick_rate": {"name": "_tick_rate", "type": TYPE_INT, "required": false},
+ }
+
+ # True if it's an server-managed authoritative match, false otherwise.
+ var _authoritative
+ var authoritative : bool:
+ get:
+ return false if not _authoritative is bool else bool(_authoritative)
+
+ #
+ var _handler_name
+ var handler_name : String:
+ get:
+ return "" if not _handler_name is String else String(_handler_name)
+
+ # Match label, if any.
+ var _label
+ var label : String:
+ get:
+ return "" if not _label is String else String(_label)
+
+ # The ID of the match, can be used to join.
+ var _match_id
+ var match_id : String:
+ get:
+ return "" if not _match_id is String else String(_match_id)
+
+ # Current number of users in the match.
+ var _size
+ var size : int:
+ get:
+ return 0 if not _size is int else int(_size)
+
+ #
+ var _tick_rate
+ var tick_rate : int:
+ get:
+ return 0 if not _tick_rate is int else int(_tick_rate)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiMatch:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiMatch", p_dict), ApiMatch) as ApiMatch
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "authoritative: %s, " % _authoritative
+ output += "handler_name: %s, " % _handler_name
+ output += "label: %s, " % _label
+ output += "match_id: %s, " % _match_id
+ output += "size: %s, " % _size
+ output += "tick_rate: %s, " % _tick_rate
+ return output
+
+# A list of realtime matches.
+class ApiMatchList extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "matches": {"name": "_matches", "type": TYPE_ARRAY, "required": false, "content": "ApiMatch"},
+ }
+
+ # A number of matches corresponding to a list operation.
+ var _matches
+ var matches : Array:
+ get:
+ return Array() if not _matches is Array else Array(_matches)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiMatchList:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiMatchList", p_dict), ApiMatchList) as ApiMatchList
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "matches: %s, " % [_matches]
+ return output
+
+# A notification in the server.
+class ApiNotification extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "code": {"name": "_code", "type": TYPE_INT, "required": false},
+ "content": {"name": "_content", "type": TYPE_STRING, "required": false},
+ "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false},
+ "id": {"name": "_id", "type": TYPE_STRING, "required": false},
+ "persistent": {"name": "_persistent", "type": TYPE_BOOL, "required": false},
+ "sender_id": {"name": "_sender_id", "type": TYPE_STRING, "required": false},
+ "subject": {"name": "_subject", "type": TYPE_STRING, "required": false},
+ }
+
+ # Category code for this notification.
+ var _code
+ var code : int:
+ get:
+ return 0 if not _code is int else int(_code)
+
+ # Content of the notification in JSON.
+ var _content
+ var content : String:
+ get:
+ return "" if not _content is String else String(_content)
+
+ # The UNIX time when the notification was created.
+ var _create_time
+ var create_time : String:
+ get:
+ return "" if not _create_time is String else String(_create_time)
+
+ # ID of the Notification.
+ var _id
+ var id : String:
+ get:
+ return "" if not _id is String else String(_id)
+
+ # True if this notification was persisted to the database.
+ var _persistent
+ var persistent : bool:
+ get:
+ return false if not _persistent is bool else bool(_persistent)
+
+ # ID of the sender, if a user. Otherwise 'null'.
+ var _sender_id
+ var sender_id : String:
+ get:
+ return "" if not _sender_id is String else String(_sender_id)
+
+ # Subject of the notification.
+ var _subject
+ var subject : String:
+ get:
+ return "" if not _subject is String else String(_subject)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiNotification:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiNotification", p_dict), ApiNotification) as ApiNotification
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "code: %s, " % _code
+ output += "content: %s, " % _content
+ output += "create_time: %s, " % _create_time
+ output += "id: %s, " % _id
+ output += "persistent: %s, " % _persistent
+ output += "sender_id: %s, " % _sender_id
+ output += "subject: %s, " % _subject
+ return output
+
+# A collection of zero or more notifications.
+class ApiNotificationList extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "cacheable_cursor": {"name": "_cacheable_cursor", "type": TYPE_STRING, "required": false},
+ "notifications": {"name": "_notifications", "type": TYPE_ARRAY, "required": false, "content": "ApiNotification"},
+ }
+
+ # Use this cursor to paginate notifications. Cache this to catch up to new notifications.
+ var _cacheable_cursor
+ var cacheable_cursor : String:
+ get:
+ return "" if not _cacheable_cursor is String else String(_cacheable_cursor)
+
+ # Collection of notifications.
+ var _notifications
+ var notifications : Array:
+ get:
+ return Array() if not _notifications is Array else Array(_notifications)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiNotificationList:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiNotificationList", p_dict), ApiNotificationList) as ApiNotificationList
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "cacheable_cursor: %s, " % _cacheable_cursor
+ output += "notifications: %s, " % [_notifications]
+ return output
+
+# Operator that can be used to override the one set in the leaderboard.
+# - NO_OVERRIDE: Do not override the leaderboard operator.
+# - BEST: Override the leaderboard operator with BEST.
+# - SET: Override the leaderboard operator with SET.
+# - INCREMENT: Override the leaderboard operator with INCREMENT.
+# - DECREMENT: Override the leaderboard operator with DECREMENT.# [ - NO_OVERRIDE: Do not override the leaderboard operator. - BEST: Override the leaderboard operator with BEST. - SET: Override the leaderboard operator with SET. - INCREMENT: Override the leaderboard operator with INCREMENT. - DECREMENT: Override the leaderboard operator with DECREMENT.]
+enum ApiOperator {NO_OVERRIDE = 0,BEST = 1,SET = 2,INCREMENT = 3,DECREMENT = 4,}
+
+# Storage objects to get.
+class ApiReadStorageObjectId extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "collection": {"name": "_collection", "type": TYPE_STRING, "required": false},
+ "key": {"name": "_key", "type": TYPE_STRING, "required": false},
+ "user_id": {"name": "_user_id", "type": TYPE_STRING, "required": false},
+ }
+
+ # The collection which stores the object.
+ var _collection
+ var collection : String:
+ get:
+ return "" if not _collection is String else String(_collection)
+
+ # The key of the object within the collection.
+ var _key
+ var key : String:
+ get:
+ return "" if not _key is String else String(_key)
+
+ # The user owner of the object.
+ var _user_id
+ var user_id : String:
+ get:
+ return "" if not _user_id is String else String(_user_id)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiReadStorageObjectId:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiReadStorageObjectId", p_dict), ApiReadStorageObjectId) as ApiReadStorageObjectId
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "collection: %s, " % _collection
+ output += "key: %s, " % _key
+ output += "user_id: %s, " % _user_id
+ return output
+
+# Batch get storage objects.
+class ApiReadStorageObjectsRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "object_ids": {"name": "_object_ids", "type": TYPE_ARRAY, "required": false, "content": "ApiReadStorageObjectId"},
+ }
+
+ # Batch of storage objects.
+ var _object_ids
+ var object_ids : Array:
+ get:
+ return Array() if not _object_ids is Array else Array(_object_ids)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiReadStorageObjectsRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiReadStorageObjectsRequest", p_dict), ApiReadStorageObjectsRequest) as ApiReadStorageObjectsRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "object_ids: %s, " % [_object_ids]
+ return output
+
+# Execute an Lua function on the server.
+class ApiRpc extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "http_key": {"name": "_http_key", "type": TYPE_STRING, "required": false},
+ "id": {"name": "_id", "type": TYPE_STRING, "required": false},
+ "payload": {"name": "_payload", "type": TYPE_STRING, "required": false},
+ }
+
+ # The authentication key used when executed as a non-client HTTP request.
+ var _http_key
+ var http_key : String:
+ get:
+ return "" if not _http_key is String else String(_http_key)
+
+ # The identifier of the function.
+ var _id
+ var id : String:
+ get:
+ return "" if not _id is String else String(_id)
+
+ # The payload of the function which must be a JSON object.
+ var _payload
+ var payload : String:
+ get:
+ return "" if not _payload is String else String(_payload)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiRpc:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiRpc", p_dict), ApiRpc) as ApiRpc
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "http_key: %s, " % _http_key
+ output += "id: %s, " % _id
+ output += "payload: %s, " % _payload
+ return output
+
+# A user's session used to authenticate messages.
+class ApiSession extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "created": {"name": "_created", "type": TYPE_BOOL, "required": false},
+ "refresh_token": {"name": "_refresh_token", "type": TYPE_STRING, "required": false},
+ "token": {"name": "_token", "type": TYPE_STRING, "required": false},
+ }
+
+ # True if the corresponding account was just created, false otherwise.
+ var _created
+ var created : bool:
+ get:
+ return false if not _created is bool else bool(_created)
+
+ # Refresh token that can be used for session token renewal.
+ var _refresh_token
+ var refresh_token : String:
+ get:
+ return "" if not _refresh_token is String else String(_refresh_token)
+
+ # Authentication credentials.
+ var _token
+ var token : String:
+ get:
+ return "" if not _token is String else String(_token)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiSession:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiSession", p_dict), ApiSession) as ApiSession
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "created: %s, " % _created
+ output += "refresh_token: %s, " % _refresh_token
+ output += "token: %s, " % _token
+ return output
+
+# Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user.
+class ApiSessionLogoutRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "refresh_token": {"name": "_refresh_token", "type": TYPE_STRING, "required": false},
+ "token": {"name": "_token", "type": TYPE_STRING, "required": false},
+ }
+
+ # Refresh token to invalidate.
+ var _refresh_token
+ var refresh_token : String:
+ get:
+ return "" if not _refresh_token is String else String(_refresh_token)
+
+ # Session token to log out.
+ var _token
+ var token : String:
+ get:
+ return "" if not _token is String else String(_token)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiSessionLogoutRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiSessionLogoutRequest", p_dict), ApiSessionLogoutRequest) as ApiSessionLogoutRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "refresh_token: %s, " % _refresh_token
+ output += "token: %s, " % _token
+ return output
+
+# Authenticate against the server with a refresh token.
+class ApiSessionRefreshRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "token": {"name": "_token", "type": TYPE_STRING, "required": false},
+ "vars": {"name": "_vars", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ }
+
+ # Refresh token.
+ var _token
+ var token : String:
+ get:
+ return "" if not _token is String else String(_token)
+
+ # Extra information that will be bundled in the session token.
+ var _vars
+ var vars : Dictionary:
+ get:
+ return Dictionary() if not _vars is Dictionary else _vars.duplicate()
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiSessionRefreshRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiSessionRefreshRequest", p_dict), ApiSessionRefreshRequest) as ApiSessionRefreshRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "token: %s, " % _token
+ var map_string : String = ""
+ if typeof(_vars) == TYPE_DICTIONARY:
+ for k in _vars:
+ map_string += "{%s=%s}, " % [k, _vars[k]]
+ output += "vars: [%s], " % map_string
+ return output
+
+# An object within the storage engine.
+class ApiStorageObject extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "collection": {"name": "_collection", "type": TYPE_STRING, "required": false},
+ "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false},
+ "key": {"name": "_key", "type": TYPE_STRING, "required": false},
+ "permission_read": {"name": "_permission_read", "type": TYPE_INT, "required": false},
+ "permission_write": {"name": "_permission_write", "type": TYPE_INT, "required": false},
+ "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false},
+ "user_id": {"name": "_user_id", "type": TYPE_STRING, "required": false},
+ "value": {"name": "_value", "type": TYPE_STRING, "required": false},
+ "version": {"name": "_version", "type": TYPE_STRING, "required": false},
+ }
+
+ # The collection which stores the object.
+ var _collection
+ var collection : String:
+ get:
+ return "" if not _collection is String else String(_collection)
+
+ # The UNIX time when the object was created.
+ var _create_time
+ var create_time : String:
+ get:
+ return "" if not _create_time is String else String(_create_time)
+
+ # The key of the object within the collection.
+ var _key
+ var key : String:
+ get:
+ return "" if not _key is String else String(_key)
+
+ # The read access permissions for the object.
+ var _permission_read
+ var permission_read : int:
+ get:
+ return 0 if not _permission_read is int else int(_permission_read)
+
+ # The write access permissions for the object.
+ var _permission_write
+ var permission_write : int:
+ get:
+ return 0 if not _permission_write is int else int(_permission_write)
+
+ # The UNIX time when the object was last updated.
+ var _update_time
+ var update_time : String:
+ get:
+ return "" if not _update_time is String else String(_update_time)
+
+ # The user owner of the object.
+ var _user_id
+ var user_id : String:
+ get:
+ return "" if not _user_id is String else String(_user_id)
+
+ # The value of the object.
+ var _value
+ var value : String:
+ get:
+ return "" if not _value is String else String(_value)
+
+ # The version hash of the object.
+ var _version
+ var version : String:
+ get:
+ return "" if not _version is String else String(_version)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiStorageObject:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiStorageObject", p_dict), ApiStorageObject) as ApiStorageObject
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "collection: %s, " % _collection
+ output += "create_time: %s, " % _create_time
+ output += "key: %s, " % _key
+ output += "permission_read: %s, " % _permission_read
+ output += "permission_write: %s, " % _permission_write
+ output += "update_time: %s, " % _update_time
+ output += "user_id: %s, " % _user_id
+ output += "value: %s, " % _value
+ output += "version: %s, " % _version
+ return output
+
+# A storage acknowledgement.
+class ApiStorageObjectAck extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "collection": {"name": "_collection", "type": TYPE_STRING, "required": false},
+ "key": {"name": "_key", "type": TYPE_STRING, "required": false},
+ "user_id": {"name": "_user_id", "type": TYPE_STRING, "required": false},
+ "version": {"name": "_version", "type": TYPE_STRING, "required": false},
+ }
+
+ # The collection which stores the object.
+ var _collection
+ var collection : String:
+ get:
+ return "" if not _collection is String else String(_collection)
+
+ # The key of the object within the collection.
+ var _key
+ var key : String:
+ get:
+ return "" if not _key is String else String(_key)
+
+ # The owner of the object.
+ var _user_id
+ var user_id : String:
+ get:
+ return "" if not _user_id is String else String(_user_id)
+
+ # The version hash of the object.
+ var _version
+ var version : String:
+ get:
+ return "" if not _version is String else String(_version)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiStorageObjectAck:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiStorageObjectAck", p_dict), ApiStorageObjectAck) as ApiStorageObjectAck
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "collection: %s, " % _collection
+ output += "key: %s, " % _key
+ output += "user_id: %s, " % _user_id
+ output += "version: %s, " % _version
+ return output
+
+# Batch of acknowledgements for the storage object write.
+class ApiStorageObjectAcks extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "acks": {"name": "_acks", "type": TYPE_ARRAY, "required": false, "content": "ApiStorageObjectAck"},
+ }
+
+ # Batch of storage write acknowledgements.
+ var _acks
+ var acks : Array:
+ get:
+ return Array() if not _acks is Array else Array(_acks)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiStorageObjectAcks:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiStorageObjectAcks", p_dict), ApiStorageObjectAcks) as ApiStorageObjectAcks
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "acks: %s, " % [_acks]
+ return output
+
+# List of storage objects.
+class ApiStorageObjectList extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false},
+ "objects": {"name": "_objects", "type": TYPE_ARRAY, "required": false, "content": "ApiStorageObject"},
+ }
+
+ # The cursor for the next page of results, if any.
+ var _cursor
+ var cursor : String:
+ get:
+ return "" if not _cursor is String else String(_cursor)
+
+ # The list of storage objects.
+ var _objects
+ var objects : Array:
+ get:
+ return Array() if not _objects is Array else Array(_objects)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiStorageObjectList:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiStorageObjectList", p_dict), ApiStorageObjectList) as ApiStorageObjectList
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "cursor: %s, " % _cursor
+ output += "objects: %s, " % [_objects]
+ return output
+
+# Batch of storage objects.
+class ApiStorageObjects extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "objects": {"name": "_objects", "type": TYPE_ARRAY, "required": false, "content": "ApiStorageObject"},
+ }
+
+ # The batch of storage objects.
+ var _objects
+ var objects : Array:
+ get:
+ return Array() if not _objects is Array else Array(_objects)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiStorageObjects:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiStorageObjects", p_dict), ApiStorageObjects) as ApiStorageObjects
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "objects: %s, " % [_objects]
+ return output
+
+# Environment where a purchase/subscription took place,
+# - UNKNOWN: Unknown environment.
+# - SANDBOX: Sandbox/test environment.
+# - PRODUCTION: Production environment.# [- UNKNOWN: Unknown environment. - SANDBOX: Sandbox/test environment. - PRODUCTION: Production environment.]
+enum ApiStoreEnvironment {UNKNOWN = 0,SANDBOX = 1,PRODUCTION = 2,}
+
+# Validation Provider,
+# - APPLE_APP_STORE: Apple App Store
+# - GOOGLE_PLAY_STORE: Google Play Store
+# - HUAWEI_APP_GALLERY: Huawei App Gallery# [- APPLE_APP_STORE: Apple App Store - GOOGLE_PLAY_STORE: Google Play Store - HUAWEI_APP_GALLERY: Huawei App Gallery]
+enum ApiStoreProvider {APPLE_APP_STORE = 0,GOOGLE_PLAY_STORE = 1,HUAWEI_APP_GALLERY = 2,}
+
+# A list of validated subscriptions stored by Nakama.
+class ApiSubscriptionList extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false},
+ "prev_cursor": {"name": "_prev_cursor", "type": TYPE_STRING, "required": false},
+ "validated_subscriptions": {"name": "_validated_subscriptions", "type": TYPE_ARRAY, "required": false, "content": "ApiValidatedSubscription"},
+ }
+
+ # The cursor to send when retrieving the next page, if any.
+ var _cursor
+ var cursor : String:
+ get:
+ return "" if not _cursor is String else String(_cursor)
+
+ # The cursor to send when retrieving the previous page, if any.
+ var _prev_cursor
+ var prev_cursor : String:
+ get:
+ return "" if not _prev_cursor is String else String(_prev_cursor)
+
+ # Stored validated subscriptions.
+ var _validated_subscriptions
+ var validated_subscriptions : Array:
+ get:
+ return Array() if not _validated_subscriptions is Array else Array(_validated_subscriptions)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiSubscriptionList:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiSubscriptionList", p_dict), ApiSubscriptionList) as ApiSubscriptionList
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "cursor: %s, " % _cursor
+ output += "prev_cursor: %s, " % _prev_cursor
+ output += "validated_subscriptions: %s, " % [_validated_subscriptions]
+ return output
+
+# A tournament on the server.
+class ApiTournament extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "authoritative": {"name": "_authoritative", "type": TYPE_BOOL, "required": false},
+ "can_enter": {"name": "_can_enter", "type": TYPE_BOOL, "required": false},
+ "category": {"name": "_category", "type": TYPE_INT, "required": false},
+ "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false},
+ "description": {"name": "_description", "type": TYPE_STRING, "required": false},
+ "duration": {"name": "_duration", "type": TYPE_INT, "required": false},
+ "end_active": {"name": "_end_active", "type": TYPE_INT, "required": false},
+ "end_time": {"name": "_end_time", "type": TYPE_STRING, "required": false},
+ "id": {"name": "_id", "type": TYPE_STRING, "required": false},
+ "max_num_score": {"name": "_max_num_score", "type": TYPE_INT, "required": false},
+ "max_size": {"name": "_max_size", "type": TYPE_INT, "required": false},
+ "metadata": {"name": "_metadata", "type": TYPE_STRING, "required": false},
+ "next_reset": {"name": "_next_reset", "type": TYPE_INT, "required": false},
+ "operator": {"name": "_operator", "type": TYPE_INT, "required": false},
+ "prev_reset": {"name": "_prev_reset", "type": TYPE_INT, "required": false},
+ "size": {"name": "_size", "type": TYPE_INT, "required": false},
+ "sort_order": {"name": "_sort_order", "type": TYPE_INT, "required": false},
+ "start_active": {"name": "_start_active", "type": TYPE_INT, "required": false},
+ "start_time": {"name": "_start_time", "type": TYPE_STRING, "required": false},
+ "title": {"name": "_title", "type": TYPE_STRING, "required": false},
+ }
+
+ # Whether the leaderboard was created authoritatively or not.
+ var _authoritative
+ var authoritative : bool:
+ get:
+ return false if not _authoritative is bool else bool(_authoritative)
+
+ # True if the tournament is active and can enter. A computed value.
+ var _can_enter
+ var can_enter : bool:
+ get:
+ return false if not _can_enter is bool else bool(_can_enter)
+
+ # The category of the tournament. e.g. "vip" could be category 1.
+ var _category
+ var category : int:
+ get:
+ return 0 if not _category is int else int(_category)
+
+ # The UNIX time when the tournament was created.
+ var _create_time
+ var create_time : String:
+ get:
+ return "" if not _create_time is String else String(_create_time)
+
+ # The description of the tournament. May be blank.
+ var _description
+ var description : String:
+ get:
+ return "" if not _description is String else String(_description)
+
+ # Duration of the tournament in seconds.
+ var _duration
+ var duration : int:
+ get:
+ return 0 if not _duration is int else int(_duration)
+
+ # The UNIX time when the tournament stops being active until next reset. A computed value.
+ var _end_active
+ var end_active : int:
+ get:
+ return 0 if not _end_active is int else int(_end_active)
+
+ # The UNIX time when the tournament will be stopped.
+ var _end_time
+ var end_time : String:
+ get:
+ return "" if not _end_time is String else String(_end_time)
+
+ # The ID of the tournament.
+ var _id
+ var id : String:
+ get:
+ return "" if not _id is String else String(_id)
+
+ # The maximum score updates allowed per player for the current tournament.
+ var _max_num_score
+ var max_num_score : int:
+ get:
+ return 0 if not _max_num_score is int else int(_max_num_score)
+
+ # The maximum number of players for the tournament.
+ var _max_size
+ var max_size : int:
+ get:
+ return 0 if not _max_size is int else int(_max_size)
+
+ # Additional information stored as a JSON object.
+ var _metadata
+ var metadata : String:
+ get:
+ return "" if not _metadata is String else String(_metadata)
+
+ # The UNIX time when the tournament is next playable. A computed value.
+ var _next_reset
+ var next_reset : int:
+ get:
+ return 0 if not _next_reset is int else int(_next_reset)
+
+ # Operator.
+ var _operator
+ var operator : int:
+ get:
+ return ApiOperator.values()[0] if not ApiOperator.values().has(_operator) else _operator
+
+ # The UNIX time when the tournament was last reset. A computed value.
+ var _prev_reset
+ var prev_reset : int:
+ get:
+ return 0 if not _prev_reset is int else int(_prev_reset)
+
+ # The current number of players in the tournament.
+ var _size
+ var size : int:
+ get:
+ return 0 if not _size is int else int(_size)
+
+ # ASC (0) or DESC (1) sort mode of scores in the tournament.
+ var _sort_order
+ var sort_order : int:
+ get:
+ return 0 if not _sort_order is int else int(_sort_order)
+
+ # The UNIX time when the tournament start being active. A computed value.
+ var _start_active
+ var start_active : int:
+ get:
+ return 0 if not _start_active is int else int(_start_active)
+
+ # The UNIX time when the tournament will start.
+ var _start_time
+ var start_time : String:
+ get:
+ return "" if not _start_time is String else String(_start_time)
+
+ # The title for the tournament.
+ var _title
+ var title : String:
+ get:
+ return "" if not _title is String else String(_title)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiTournament:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiTournament", p_dict), ApiTournament) as ApiTournament
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "authoritative: %s, " % _authoritative
+ output += "can_enter: %s, " % _can_enter
+ output += "category: %s, " % _category
+ output += "create_time: %s, " % _create_time
+ output += "description: %s, " % _description
+ output += "duration: %s, " % _duration
+ output += "end_active: %s, " % _end_active
+ output += "end_time: %s, " % _end_time
+ output += "id: %s, " % _id
+ output += "max_num_score: %s, " % _max_num_score
+ output += "max_size: %s, " % _max_size
+ output += "metadata: %s, " % _metadata
+ output += "next_reset: %s, " % _next_reset
+ output += "operator: %s, " % _operator
+ output += "prev_reset: %s, " % _prev_reset
+ output += "size: %s, " % _size
+ output += "sort_order: %s, " % _sort_order
+ output += "start_active: %s, " % _start_active
+ output += "start_time: %s, " % _start_time
+ output += "title: %s, " % _title
+ return output
+
+# A list of tournaments.
+class ApiTournamentList extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false},
+ "tournaments": {"name": "_tournaments", "type": TYPE_ARRAY, "required": false, "content": "ApiTournament"},
+ }
+
+ # A pagination cursor (optional).
+ var _cursor
+ var cursor : String:
+ get:
+ return "" if not _cursor is String else String(_cursor)
+
+ # The list of tournaments returned.
+ var _tournaments
+ var tournaments : Array:
+ get:
+ return Array() if not _tournaments is Array else Array(_tournaments)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiTournamentList:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiTournamentList", p_dict), ApiTournamentList) as ApiTournamentList
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "cursor: %s, " % _cursor
+ output += "tournaments: %s, " % [_tournaments]
+ return output
+
+# A set of tournament records which may be part of a tournament records page or a batch of individual records.
+class ApiTournamentRecordList extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "next_cursor": {"name": "_next_cursor", "type": TYPE_STRING, "required": false},
+ "owner_records": {"name": "_owner_records", "type": TYPE_ARRAY, "required": false, "content": "ApiLeaderboardRecord"},
+ "prev_cursor": {"name": "_prev_cursor", "type": TYPE_STRING, "required": false},
+ "records": {"name": "_records", "type": TYPE_ARRAY, "required": false, "content": "ApiLeaderboardRecord"},
+ }
+
+ # The cursor to send when retireving the next page (optional).
+ var _next_cursor
+ var next_cursor : String:
+ get:
+ return "" if not _next_cursor is String else String(_next_cursor)
+
+ # A batched set of tournament records belonging to specified owners.
+ var _owner_records
+ var owner_records : Array:
+ get:
+ return Array() if not _owner_records is Array else Array(_owner_records)
+
+ # The cursor to send when retrieving the previous page (optional).
+ var _prev_cursor
+ var prev_cursor : String:
+ get:
+ return "" if not _prev_cursor is String else String(_prev_cursor)
+
+ # A list of tournament records.
+ var _records
+ var records : Array:
+ get:
+ return Array() if not _records is Array else Array(_records)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiTournamentRecordList:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiTournamentRecordList", p_dict), ApiTournamentRecordList) as ApiTournamentRecordList
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "next_cursor: %s, " % _next_cursor
+ output += "owner_records: %s, " % [_owner_records]
+ output += "prev_cursor: %s, " % _prev_cursor
+ output += "records: %s, " % [_records]
+ return output
+
+# Update a user's account details.
+class ApiUpdateAccountRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "avatar_url": {"name": "_avatar_url", "type": TYPE_STRING, "required": false},
+ "display_name": {"name": "_display_name", "type": TYPE_STRING, "required": false},
+ "lang_tag": {"name": "_lang_tag", "type": TYPE_STRING, "required": false},
+ "location": {"name": "_location", "type": TYPE_STRING, "required": false},
+ "timezone": {"name": "_timezone", "type": TYPE_STRING, "required": false},
+ "username": {"name": "_username", "type": TYPE_STRING, "required": false},
+ }
+
+ # A URL for an avatar image.
+ var _avatar_url
+ var avatar_url : String:
+ get:
+ return "" if not _avatar_url is String else String(_avatar_url)
+
+ # The display name of the user.
+ var _display_name
+ var display_name : String:
+ get:
+ return "" if not _display_name is String else String(_display_name)
+
+ # The language expected to be a tag which follows the BCP-47 spec.
+ var _lang_tag
+ var lang_tag : String:
+ get:
+ return "" if not _lang_tag is String else String(_lang_tag)
+
+ # The location set by the user.
+ var _location
+ var location : String:
+ get:
+ return "" if not _location is String else String(_location)
+
+ # The timezone set by the user.
+ var _timezone
+ var timezone : String:
+ get:
+ return "" if not _timezone is String else String(_timezone)
+
+ # The username of the user's account.
+ var _username
+ var username : String:
+ get:
+ return "" if not _username is String else String(_username)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiUpdateAccountRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiUpdateAccountRequest", p_dict), ApiUpdateAccountRequest) as ApiUpdateAccountRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "avatar_url: %s, " % _avatar_url
+ output += "display_name: %s, " % _display_name
+ output += "lang_tag: %s, " % _lang_tag
+ output += "location: %s, " % _location
+ output += "timezone: %s, " % _timezone
+ output += "username: %s, " % _username
+ return output
+
+# Update fields in a given group.
+class ApiUpdateGroupRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "avatar_url": {"name": "_avatar_url", "type": TYPE_STRING, "required": false},
+ "description": {"name": "_description", "type": TYPE_STRING, "required": false},
+ "group_id": {"name": "_group_id", "type": TYPE_STRING, "required": false},
+ "lang_tag": {"name": "_lang_tag", "type": TYPE_STRING, "required": false},
+ "name": {"name": "_name", "type": TYPE_STRING, "required": false},
+ "open": {"name": "_open", "type": TYPE_BOOL, "required": false},
+ }
+
+ # Avatar URL.
+ var _avatar_url
+ var avatar_url : String:
+ get:
+ return "" if not _avatar_url is String else String(_avatar_url)
+
+ # Description string.
+ var _description
+ var description : String:
+ get:
+ return "" if not _description is String else String(_description)
+
+ # The ID of the group to update.
+ var _group_id
+ var group_id : String:
+ get:
+ return "" if not _group_id is String else String(_group_id)
+
+ # Lang tag.
+ var _lang_tag
+ var lang_tag : String:
+ get:
+ return "" if not _lang_tag is String else String(_lang_tag)
+
+ # Name.
+ var _name
+ var name : String:
+ get:
+ return "" if not _name is String else String(_name)
+
+ # Open is true if anyone should be allowed to join, or false if joins must be approved by a group admin.
+ var _open
+ var open : bool:
+ get:
+ return false if not _open is bool else bool(_open)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiUpdateGroupRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiUpdateGroupRequest", p_dict), ApiUpdateGroupRequest) as ApiUpdateGroupRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "avatar_url: %s, " % _avatar_url
+ output += "description: %s, " % _description
+ output += "group_id: %s, " % _group_id
+ output += "lang_tag: %s, " % _lang_tag
+ output += "name: %s, " % _name
+ output += "open: %s, " % _open
+ return output
+
+# A user in the server.
+class ApiUser extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "apple_id": {"name": "_apple_id", "type": TYPE_STRING, "required": false},
+ "avatar_url": {"name": "_avatar_url", "type": TYPE_STRING, "required": false},
+ "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false},
+ "display_name": {"name": "_display_name", "type": TYPE_STRING, "required": false},
+ "edge_count": {"name": "_edge_count", "type": TYPE_INT, "required": false},
+ "facebook_id": {"name": "_facebook_id", "type": TYPE_STRING, "required": false},
+ "facebook_instant_game_id": {"name": "_facebook_instant_game_id", "type": TYPE_STRING, "required": false},
+ "gamecenter_id": {"name": "_gamecenter_id", "type": TYPE_STRING, "required": false},
+ "google_id": {"name": "_google_id", "type": TYPE_STRING, "required": false},
+ "id": {"name": "_id", "type": TYPE_STRING, "required": false},
+ "lang_tag": {"name": "_lang_tag", "type": TYPE_STRING, "required": false},
+ "location": {"name": "_location", "type": TYPE_STRING, "required": false},
+ "metadata": {"name": "_metadata", "type": TYPE_STRING, "required": false},
+ "online": {"name": "_online", "type": TYPE_BOOL, "required": false},
+ "steam_id": {"name": "_steam_id", "type": TYPE_STRING, "required": false},
+ "timezone": {"name": "_timezone", "type": TYPE_STRING, "required": false},
+ "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false},
+ "username": {"name": "_username", "type": TYPE_STRING, "required": false},
+ }
+
+ # The Apple Sign In ID in the user's account.
+ var _apple_id
+ var apple_id : String:
+ get:
+ return "" if not _apple_id is String else String(_apple_id)
+
+ # A URL for an avatar image.
+ var _avatar_url
+ var avatar_url : String:
+ get:
+ return "" if not _avatar_url is String else String(_avatar_url)
+
+ # The UNIX time when the user was created.
+ var _create_time
+ var create_time : String:
+ get:
+ return "" if not _create_time is String else String(_create_time)
+
+ # The display name of the user.
+ var _display_name
+ var display_name : String:
+ get:
+ return "" if not _display_name is String else String(_display_name)
+
+ # Number of related edges to this user.
+ var _edge_count
+ var edge_count : int:
+ get:
+ return 0 if not _edge_count is int else int(_edge_count)
+
+ # The Facebook id in the user's account.
+ var _facebook_id
+ var facebook_id : String:
+ get:
+ return "" if not _facebook_id is String else String(_facebook_id)
+
+ # The Facebook Instant Game ID in the user's account.
+ var _facebook_instant_game_id
+ var facebook_instant_game_id : String:
+ get:
+ return "" if not _facebook_instant_game_id is String else String(_facebook_instant_game_id)
+
+ # The Apple Game Center in of the user's account.
+ var _gamecenter_id
+ var gamecenter_id : String:
+ get:
+ return "" if not _gamecenter_id is String else String(_gamecenter_id)
+
+ # The Google id in the user's account.
+ var _google_id
+ var google_id : String:
+ get:
+ return "" if not _google_id is String else String(_google_id)
+
+ # The id of the user's account.
+ var _id
+ var id : String:
+ get:
+ return "" if not _id is String else String(_id)
+
+ # The language expected to be a tag which follows the BCP-47 spec.
+ var _lang_tag
+ var lang_tag : String:
+ get:
+ return "" if not _lang_tag is String else String(_lang_tag)
+
+ # The location set by the user.
+ var _location
+ var location : String:
+ get:
+ return "" if not _location is String else String(_location)
+
+ # Additional information stored as a JSON object.
+ var _metadata
+ var metadata : String:
+ get:
+ return "" if not _metadata is String else String(_metadata)
+
+ # Indicates whether the user is currently online.
+ var _online
+ var online : bool:
+ get:
+ return false if not _online is bool else bool(_online)
+
+ # The Steam id in the user's account.
+ var _steam_id
+ var steam_id : String:
+ get:
+ return "" if not _steam_id is String else String(_steam_id)
+
+ # The timezone set by the user.
+ var _timezone
+ var timezone : String:
+ get:
+ return "" if not _timezone is String else String(_timezone)
+
+ # The UNIX time when the user was last updated.
+ var _update_time
+ var update_time : String:
+ get:
+ return "" if not _update_time is String else String(_update_time)
+
+ # The username of the user's account.
+ var _username
+ var username : String:
+ get:
+ return "" if not _username is String else String(_username)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiUser:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiUser", p_dict), ApiUser) as ApiUser
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "apple_id: %s, " % _apple_id
+ output += "avatar_url: %s, " % _avatar_url
+ output += "create_time: %s, " % _create_time
+ output += "display_name: %s, " % _display_name
+ output += "edge_count: %s, " % _edge_count
+ output += "facebook_id: %s, " % _facebook_id
+ output += "facebook_instant_game_id: %s, " % _facebook_instant_game_id
+ output += "gamecenter_id: %s, " % _gamecenter_id
+ output += "google_id: %s, " % _google_id
+ output += "id: %s, " % _id
+ output += "lang_tag: %s, " % _lang_tag
+ output += "location: %s, " % _location
+ output += "metadata: %s, " % _metadata
+ output += "online: %s, " % _online
+ output += "steam_id: %s, " % _steam_id
+ output += "timezone: %s, " % _timezone
+ output += "update_time: %s, " % _update_time
+ output += "username: %s, " % _username
+ return output
+
+# A list of groups belonging to a user, along with the user's role in each group.
+class ApiUserGroupList extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "cursor": {"name": "_cursor", "type": TYPE_STRING, "required": false},
+ "user_groups": {"name": "_user_groups", "type": TYPE_ARRAY, "required": false, "content": "UserGroupListUserGroup"},
+ }
+
+ # Cursor for the next page of results, if any.
+ var _cursor
+ var cursor : String:
+ get:
+ return "" if not _cursor is String else String(_cursor)
+
+ # Group-role pairs for a user.
+ var _user_groups
+ var user_groups : Array:
+ get:
+ return Array() if not _user_groups is Array else Array(_user_groups)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiUserGroupList:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiUserGroupList", p_dict), ApiUserGroupList) as ApiUserGroupList
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "cursor: %s, " % _cursor
+ output += "user_groups: %s, " % [_user_groups]
+ return output
+
+# A collection of zero or more users.
+class ApiUsers extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "users": {"name": "_users", "type": TYPE_ARRAY, "required": false, "content": "ApiUser"},
+ }
+
+ # The User objects.
+ var _users
+ var users : Array:
+ get:
+ return Array() if not _users is Array else Array(_users)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiUsers:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiUsers", p_dict), ApiUsers) as ApiUsers
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "users: %s, " % [_users]
+ return output
+
+#
+class ApiValidatePurchaseAppleRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "persist": {"name": "_persist", "type": TYPE_BOOL, "required": false},
+ "receipt": {"name": "_receipt", "type": TYPE_STRING, "required": false},
+ }
+
+ #
+ var _persist
+ var persist : bool:
+ get:
+ return false if not _persist is bool else bool(_persist)
+
+ # Base64 encoded Apple receipt data payload.
+ var _receipt
+ var receipt : String:
+ get:
+ return "" if not _receipt is String else String(_receipt)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidatePurchaseAppleRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidatePurchaseAppleRequest", p_dict), ApiValidatePurchaseAppleRequest) as ApiValidatePurchaseAppleRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "persist: %s, " % _persist
+ output += "receipt: %s, " % _receipt
+ return output
+
+#
+class ApiValidatePurchaseGoogleRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "persist": {"name": "_persist", "type": TYPE_BOOL, "required": false},
+ "purchase": {"name": "_purchase", "type": TYPE_STRING, "required": false},
+ }
+
+ #
+ var _persist
+ var persist : bool:
+ get:
+ return false if not _persist is bool else bool(_persist)
+
+ # JSON encoded Google purchase payload.
+ var _purchase
+ var purchase : String:
+ get:
+ return "" if not _purchase is String else String(_purchase)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidatePurchaseGoogleRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidatePurchaseGoogleRequest", p_dict), ApiValidatePurchaseGoogleRequest) as ApiValidatePurchaseGoogleRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "persist: %s, " % _persist
+ output += "purchase: %s, " % _purchase
+ return output
+
+#
+class ApiValidatePurchaseHuaweiRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "persist": {"name": "_persist", "type": TYPE_BOOL, "required": false},
+ "purchase": {"name": "_purchase", "type": TYPE_STRING, "required": false},
+ "signature": {"name": "_signature", "type": TYPE_STRING, "required": false},
+ }
+
+ #
+ var _persist
+ var persist : bool:
+ get:
+ return false if not _persist is bool else bool(_persist)
+
+ # JSON encoded Huawei InAppPurchaseData.
+ var _purchase
+ var purchase : String:
+ get:
+ return "" if not _purchase is String else String(_purchase)
+
+ # InAppPurchaseData signature.
+ var _signature
+ var signature : String:
+ get:
+ return "" if not _signature is String else String(_signature)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidatePurchaseHuaweiRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidatePurchaseHuaweiRequest", p_dict), ApiValidatePurchaseHuaweiRequest) as ApiValidatePurchaseHuaweiRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "persist: %s, " % _persist
+ output += "purchase: %s, " % _purchase
+ output += "signature: %s, " % _signature
+ return output
+
+# Validate IAP response.
+class ApiValidatePurchaseResponse extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "validated_purchases": {"name": "_validated_purchases", "type": TYPE_ARRAY, "required": false, "content": "ApiValidatedPurchase"},
+ }
+
+ # Newly seen validated purchases.
+ var _validated_purchases
+ var validated_purchases : Array:
+ get:
+ return Array() if not _validated_purchases is Array else Array(_validated_purchases)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidatePurchaseResponse:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidatePurchaseResponse", p_dict), ApiValidatePurchaseResponse) as ApiValidatePurchaseResponse
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "validated_purchases: %s, " % [_validated_purchases]
+ return output
+
+#
+class ApiValidateSubscriptionAppleRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "persist": {"name": "_persist", "type": TYPE_BOOL, "required": false},
+ "receipt": {"name": "_receipt", "type": TYPE_STRING, "required": false},
+ }
+
+ # Persist the subscription.
+ var _persist
+ var persist : bool:
+ get:
+ return false if not _persist is bool else bool(_persist)
+
+ # Base64 encoded Apple receipt data payload.
+ var _receipt
+ var receipt : String:
+ get:
+ return "" if not _receipt is String else String(_receipt)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidateSubscriptionAppleRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidateSubscriptionAppleRequest", p_dict), ApiValidateSubscriptionAppleRequest) as ApiValidateSubscriptionAppleRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "persist: %s, " % _persist
+ output += "receipt: %s, " % _receipt
+ return output
+
+#
+class ApiValidateSubscriptionGoogleRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "persist": {"name": "_persist", "type": TYPE_BOOL, "required": false},
+ "receipt": {"name": "_receipt", "type": TYPE_STRING, "required": false},
+ }
+
+ # Persist the subscription.
+ var _persist
+ var persist : bool:
+ get:
+ return false if not _persist is bool else bool(_persist)
+
+ # JSON encoded Google purchase payload.
+ var _receipt
+ var receipt : String:
+ get:
+ return "" if not _receipt is String else String(_receipt)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidateSubscriptionGoogleRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidateSubscriptionGoogleRequest", p_dict), ApiValidateSubscriptionGoogleRequest) as ApiValidateSubscriptionGoogleRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "persist: %s, " % _persist
+ output += "receipt: %s, " % _receipt
+ return output
+
+# Validate Subscription response.
+class ApiValidateSubscriptionResponse extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "validated_subscription": {"name": "_validated_subscription", "type": "ApiValidatedSubscription", "required": false},
+ }
+
+ #
+ var _validated_subscription
+ var validated_subscription : ApiValidatedSubscription:
+ get:
+ return _validated_subscription as ApiValidatedSubscription
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidateSubscriptionResponse:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidateSubscriptionResponse", p_dict), ApiValidateSubscriptionResponse) as ApiValidateSubscriptionResponse
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "validated_subscription: %s, " % _validated_subscription
+ return output
+
+# Validated Purchase stored by Nakama.
+class ApiValidatedPurchase extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false},
+ "environment": {"name": "_environment", "type": TYPE_INT, "required": false},
+ "product_id": {"name": "_product_id", "type": TYPE_STRING, "required": false},
+ "provider_response": {"name": "_provider_response", "type": TYPE_STRING, "required": false},
+ "purchase_time": {"name": "_purchase_time", "type": TYPE_STRING, "required": false},
+ "refund_time": {"name": "_refund_time", "type": TYPE_STRING, "required": false},
+ "seen_before": {"name": "_seen_before", "type": TYPE_BOOL, "required": false},
+ "store": {"name": "_store", "type": TYPE_INT, "required": false},
+ "transaction_id": {"name": "_transaction_id", "type": TYPE_STRING, "required": false},
+ "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false},
+ "user_id": {"name": "_user_id", "type": TYPE_STRING, "required": false},
+ }
+
+ # Timestamp when the receipt validation was stored in DB.
+ var _create_time
+ var create_time : String:
+ get:
+ return "" if not _create_time is String else String(_create_time)
+
+ # Whether the purchase was done in production or sandbox environment.
+ var _environment
+ var environment : int:
+ get:
+ return ApiStoreEnvironment.values()[0] if not ApiStoreEnvironment.values().has(_environment) else _environment
+
+ # Purchase Product ID.
+ var _product_id
+ var product_id : String:
+ get:
+ return "" if not _product_id is String else String(_product_id)
+
+ # Raw provider validation response.
+ var _provider_response
+ var provider_response : String:
+ get:
+ return "" if not _provider_response is String else String(_provider_response)
+
+ # Timestamp when the purchase was done.
+ var _purchase_time
+ var purchase_time : String:
+ get:
+ return "" if not _purchase_time is String else String(_purchase_time)
+
+ #
+ var _refund_time
+ var refund_time : String:
+ get:
+ return "" if not _refund_time is String else String(_refund_time)
+
+ # Whether the purchase had already been validated by Nakama before.
+ var _seen_before
+ var seen_before : bool:
+ get:
+ return false if not _seen_before is bool else bool(_seen_before)
+
+ #
+ var _store
+ var store : int:
+ get:
+ return ApiStoreProvider.values()[0] if not ApiStoreProvider.values().has(_store) else _store
+
+ # Purchase Transaction ID.
+ var _transaction_id
+ var transaction_id : String:
+ get:
+ return "" if not _transaction_id is String else String(_transaction_id)
+
+ # Timestamp when the receipt validation was updated in DB.
+ var _update_time
+ var update_time : String:
+ get:
+ return "" if not _update_time is String else String(_update_time)
+
+ # Purchase User ID.
+ var _user_id
+ var user_id : String:
+ get:
+ return "" if not _user_id is String else String(_user_id)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidatedPurchase:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidatedPurchase", p_dict), ApiValidatedPurchase) as ApiValidatedPurchase
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "create_time: %s, " % _create_time
+ output += "environment: %s, " % _environment
+ output += "product_id: %s, " % _product_id
+ output += "provider_response: %s, " % _provider_response
+ output += "purchase_time: %s, " % _purchase_time
+ output += "refund_time: %s, " % _refund_time
+ output += "seen_before: %s, " % _seen_before
+ output += "store: %s, " % _store
+ output += "transaction_id: %s, " % _transaction_id
+ output += "update_time: %s, " % _update_time
+ output += "user_id: %s, " % _user_id
+ return output
+
+#
+class ApiValidatedSubscription extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "active": {"name": "_active", "type": TYPE_BOOL, "required": false},
+ "create_time": {"name": "_create_time", "type": TYPE_STRING, "required": false},
+ "environment": {"name": "_environment", "type": TYPE_INT, "required": false},
+ "expiry_time": {"name": "_expiry_time", "type": TYPE_STRING, "required": false},
+ "original_transaction_id": {"name": "_original_transaction_id", "type": TYPE_STRING, "required": false},
+ "product_id": {"name": "_product_id", "type": TYPE_STRING, "required": false},
+ "provider_notification": {"name": "_provider_notification", "type": TYPE_STRING, "required": false},
+ "provider_response": {"name": "_provider_response", "type": TYPE_STRING, "required": false},
+ "purchase_time": {"name": "_purchase_time", "type": TYPE_STRING, "required": false},
+ "refund_time": {"name": "_refund_time", "type": TYPE_STRING, "required": false},
+ "store": {"name": "_store", "type": TYPE_INT, "required": false},
+ "update_time": {"name": "_update_time", "type": TYPE_STRING, "required": false},
+ "user_id": {"name": "_user_id", "type": TYPE_STRING, "required": false},
+ }
+
+ # Whether the subscription is currently active or not.
+ var _active
+ var active : bool:
+ get:
+ return false if not _active is bool else bool(_active)
+
+ # UNIX Timestamp when the receipt validation was stored in DB.
+ var _create_time
+ var create_time : String:
+ get:
+ return "" if not _create_time is String else String(_create_time)
+
+ # Whether the purchase was done in production or sandbox environment.
+ var _environment
+ var environment : int:
+ get:
+ return ApiStoreEnvironment.values()[0] if not ApiStoreEnvironment.values().has(_environment) else _environment
+
+ # Subscription expiration time. The subscription can still be auto-renewed to extend the expiration time further.
+ var _expiry_time
+ var expiry_time : String:
+ get:
+ return "" if not _expiry_time is String else String(_expiry_time)
+
+ # Purchase Original transaction ID (we only keep track of the original subscription, not subsequent renewals).
+ var _original_transaction_id
+ var original_transaction_id : String:
+ get:
+ return "" if not _original_transaction_id is String else String(_original_transaction_id)
+
+ # Purchase Product ID.
+ var _product_id
+ var product_id : String:
+ get:
+ return "" if not _product_id is String else String(_product_id)
+
+ # Raw provider notification body.
+ var _provider_notification
+ var provider_notification : String:
+ get:
+ return "" if not _provider_notification is String else String(_provider_notification)
+
+ # Raw provider validation response body.
+ var _provider_response
+ var provider_response : String:
+ get:
+ return "" if not _provider_response is String else String(_provider_response)
+
+ # UNIX Timestamp when the purchase was done.
+ var _purchase_time
+ var purchase_time : String:
+ get:
+ return "" if not _purchase_time is String else String(_purchase_time)
+
+ # Subscription refund time. If this time is set, the subscription was refunded.
+ var _refund_time
+ var refund_time : String:
+ get:
+ return "" if not _refund_time is String else String(_refund_time)
+
+ #
+ var _store
+ var store : int:
+ get:
+ return ApiStoreProvider.values()[0] if not ApiStoreProvider.values().has(_store) else _store
+
+ # UNIX Timestamp when the receipt validation was updated in DB.
+ var _update_time
+ var update_time : String:
+ get:
+ return "" if not _update_time is String else String(_update_time)
+
+ # Subscription User ID.
+ var _user_id
+ var user_id : String:
+ get:
+ return "" if not _user_id is String else String(_user_id)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiValidatedSubscription:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiValidatedSubscription", p_dict), ApiValidatedSubscription) as ApiValidatedSubscription
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "active: %s, " % _active
+ output += "create_time: %s, " % _create_time
+ output += "environment: %s, " % _environment
+ output += "expiry_time: %s, " % _expiry_time
+ output += "original_transaction_id: %s, " % _original_transaction_id
+ output += "product_id: %s, " % _product_id
+ output += "provider_notification: %s, " % _provider_notification
+ output += "provider_response: %s, " % _provider_response
+ output += "purchase_time: %s, " % _purchase_time
+ output += "refund_time: %s, " % _refund_time
+ output += "store: %s, " % _store
+ output += "update_time: %s, " % _update_time
+ output += "user_id: %s, " % _user_id
+ return output
+
+# The object to store.
+class ApiWriteStorageObject extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "collection": {"name": "_collection", "type": TYPE_STRING, "required": false},
+ "key": {"name": "_key", "type": TYPE_STRING, "required": false},
+ "permission_read": {"name": "_permission_read", "type": TYPE_INT, "required": false},
+ "permission_write": {"name": "_permission_write", "type": TYPE_INT, "required": false},
+ "value": {"name": "_value", "type": TYPE_STRING, "required": false},
+ "version": {"name": "_version", "type": TYPE_STRING, "required": false},
+ }
+
+ # The collection to store the object.
+ var _collection
+ var collection : String:
+ get:
+ return "" if not _collection is String else String(_collection)
+
+ # The key for the object within the collection.
+ var _key
+ var key : String:
+ get:
+ return "" if not _key is String else String(_key)
+
+ # The read access permissions for the object.
+ var _permission_read
+ var permission_read : int:
+ get:
+ return 0 if not _permission_read is int else int(_permission_read)
+
+ # The write access permissions for the object.
+ var _permission_write
+ var permission_write : int:
+ get:
+ return 0 if not _permission_write is int else int(_permission_write)
+
+ # The value of the object.
+ var _value
+ var value : String:
+ get:
+ return "" if not _value is String else String(_value)
+
+ # The version hash of the object to check. Possible values are: ["", "*", "#hash#"].
+ var _version
+ var version : String:
+ get:
+ return "" if not _version is String else String(_version)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiWriteStorageObject:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiWriteStorageObject", p_dict), ApiWriteStorageObject) as ApiWriteStorageObject
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "collection: %s, " % _collection
+ output += "key: %s, " % _key
+ output += "permission_read: %s, " % _permission_read
+ output += "permission_write: %s, " % _permission_write
+ output += "value: %s, " % _value
+ output += "version: %s, " % _version
+ return output
+
+# Write objects to the storage engine.
+class ApiWriteStorageObjectsRequest extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "objects": {"name": "_objects", "type": TYPE_ARRAY, "required": false, "content": "ApiWriteStorageObject"},
+ }
+
+ # The objects to store on the server.
+ var _objects
+ var objects : Array:
+ get:
+ return Array() if not _objects is Array else Array(_objects)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ApiWriteStorageObjectsRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ApiWriteStorageObjectsRequest", p_dict), ApiWriteStorageObjectsRequest) as ApiWriteStorageObjectsRequest
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "objects: %s, " % [_objects]
+ return output
+
+#
+class ProtobufAny extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "type_url": {"name": "_type_url", "type": TYPE_STRING, "required": false},
+ "value": {"name": "_value", "type": TYPE_STRING, "required": false},
+ }
+
+ #
+ var _type_url
+ var type_url : String:
+ get:
+ return "" if not _type_url is String else String(_type_url)
+
+ #
+ var _value
+ var value : String:
+ get:
+ return "" if not _value is String else String(_value)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ProtobufAny:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ProtobufAny", p_dict), ProtobufAny) as ProtobufAny
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "type_url: %s, " % _type_url
+ output += "value: %s, " % _value
+ return output
+
+#
+class RpcStatus extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "code": {"name": "_code", "type": TYPE_INT, "required": false},
+ "details": {"name": "_details", "type": TYPE_ARRAY, "required": false, "content": "ProtobufAny"},
+ "message": {"name": "_message", "type": TYPE_STRING, "required": false},
+ }
+
+ #
+ var _code
+ var code : int:
+ get:
+ return 0 if not _code is int else int(_code)
+
+ #
+ var _details
+ var details : Array:
+ get:
+ return Array() if not _details is Array else Array(_details)
+
+ #
+ var _message
+ var message : String:
+ get:
+ return "" if not _message is String else String(_message)
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> RpcStatus:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "RpcStatus", p_dict), RpcStatus) as RpcStatus
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ output += "code: %s, " % _code
+ output += "details: %s, " % [_details]
+ output += "message: %s, " % _message
+ return output
+
+# The low level client for the Nakama API.
+class ApiClient extends RefCounted:
+
+ var _base_uri : String
+
+ var _http_adapter
+ var _namespace : GDScript
+ var _server_key : String
+ var auto_refresh := true
+ var auto_refresh_time := 300
+
+ var auto_retry : bool:
+ set(p_value):
+ _http_adapter.auto_retry = p_value
+ get:
+ return _http_adapter.auto_retry
+
+ var auto_retry_count : int:
+ set(p_value):
+ _http_adapter.auto_retry_count = p_value
+ get:
+ return _http_adapter.auto_retry_count
+
+ var auto_retry_backoff_base : int:
+ set(p_value):
+ _http_adapter.auto_retry_backoff_base = p_value
+ get:
+ return _http_adapter.auto_retry_backoff_base
+
+ var last_cancel_token:
+ get:
+ return _http_adapter.get_last_token()
+
+ func _init(p_base_uri : String, p_http_adapter, p_namespace : GDScript, p_server_key : String, p_timeout : int = 10):
+ _base_uri = p_base_uri
+ _http_adapter = p_http_adapter
+ _http_adapter.timeout = p_timeout
+ _namespace = p_namespace
+ _server_key = p_server_key
+
+ func _refresh_session(p_session : NakamaSession):
+ if auto_refresh and p_session.is_valid() and p_session.refresh_token and not p_session.is_refresh_expired() and p_session.would_expire_in(auto_refresh_time):
+ var request = ApiSessionRefreshRequest.new()
+ request._token = p_session.refresh_token
+ return await session_refresh_async(_server_key, "", request)
+ return null
+
+ func cancel_request(p_token):
+ if p_token:
+ _http_adapter.cancel_request(p_token)
+
+ # A healthcheck which load balancers can use to check the service.
+ func healthcheck_async(
+ p_session : NakamaSession
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/healthcheck"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Delete the current user's account.
+ func delete_account_async(
+ p_session : NakamaSession
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "DELETE"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Fetch the current user's account.
+ func get_account_async(
+ p_session : NakamaSession
+ ) -> ApiAccount:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiAccount.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiAccount.new(result)
+ var out : ApiAccount = NakamaSerializer.deserialize(_namespace, "ApiAccount", result)
+ return out
+
+ # Update fields in the current user's account.
+ func update_account_async(
+ p_session : NakamaSession
+ , p_body : ApiUpdateAccountRequest
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "PUT"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Authenticate a user with an Apple ID against the server.
+ func authenticate_apple_async(
+ p_basic_auth_username : String
+ , p_basic_auth_password : String
+ , p_account : ApiAccountApple
+ , p_create = null # : boolean
+ , p_username = null # : string
+ ) -> ApiSession:
+ var urlpath : String = "/v2/account/authenticate/apple"
+ var query_params = ""
+ if p_create != null:
+ query_params += "create=%s&" % str(bool(p_create)).to_lower()
+ if p_username != null:
+ query_params += "username=%s&" % NakamaSerializer.escape_http(p_username)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password)
+ var header = "Basic %s" % credentials
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_account.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiSession.new(result)
+ var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result)
+ return out
+
+ # Authenticate a user with a custom id against the server.
+ func authenticate_custom_async(
+ p_basic_auth_username : String
+ , p_basic_auth_password : String
+ , p_account : ApiAccountCustom
+ , p_create = null # : boolean
+ , p_username = null # : string
+ ) -> ApiSession:
+ var urlpath : String = "/v2/account/authenticate/custom"
+ var query_params = ""
+ if p_create != null:
+ query_params += "create=%s&" % str(bool(p_create)).to_lower()
+ if p_username != null:
+ query_params += "username=%s&" % NakamaSerializer.escape_http(p_username)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password)
+ var header = "Basic %s" % credentials
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_account.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiSession.new(result)
+ var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result)
+ return out
+
+ # Authenticate a user with a device id against the server.
+ func authenticate_device_async(
+ p_basic_auth_username : String
+ , p_basic_auth_password : String
+ , p_account : ApiAccountDevice
+ , p_create = null # : boolean
+ , p_username = null # : string
+ ) -> ApiSession:
+ var urlpath : String = "/v2/account/authenticate/device"
+ var query_params = ""
+ if p_create != null:
+ query_params += "create=%s&" % str(bool(p_create)).to_lower()
+ if p_username != null:
+ query_params += "username=%s&" % NakamaSerializer.escape_http(p_username)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password)
+ var header = "Basic %s" % credentials
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_account.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiSession.new(result)
+ var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result)
+ return out
+
+ # Authenticate a user with an email+password against the server.
+ func authenticate_email_async(
+ p_basic_auth_username : String
+ , p_basic_auth_password : String
+ , p_account : ApiAccountEmail
+ , p_create = null # : boolean
+ , p_username = null # : string
+ ) -> ApiSession:
+ var urlpath : String = "/v2/account/authenticate/email"
+ var query_params = ""
+ if p_create != null:
+ query_params += "create=%s&" % str(bool(p_create)).to_lower()
+ if p_username != null:
+ query_params += "username=%s&" % NakamaSerializer.escape_http(p_username)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password)
+ var header = "Basic %s" % credentials
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_account.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiSession.new(result)
+ var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result)
+ return out
+
+ # Authenticate a user with a Facebook OAuth token against the server.
+ func authenticate_facebook_async(
+ p_basic_auth_username : String
+ , p_basic_auth_password : String
+ , p_account : ApiAccountFacebook
+ , p_create = null # : boolean
+ , p_username = null # : string
+ , p_sync = null # : boolean
+ ) -> ApiSession:
+ var urlpath : String = "/v2/account/authenticate/facebook"
+ var query_params = ""
+ if p_create != null:
+ query_params += "create=%s&" % str(bool(p_create)).to_lower()
+ if p_username != null:
+ query_params += "username=%s&" % NakamaSerializer.escape_http(p_username)
+ if p_sync != null:
+ query_params += "sync=%s&" % str(bool(p_sync)).to_lower()
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password)
+ var header = "Basic %s" % credentials
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_account.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiSession.new(result)
+ var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result)
+ return out
+
+ # Authenticate a user with a Facebook Instant Game token against the server.
+ func authenticate_facebook_instant_game_async(
+ p_basic_auth_username : String
+ , p_basic_auth_password : String
+ , p_account : ApiAccountFacebookInstantGame
+ , p_create = null # : boolean
+ , p_username = null # : string
+ ) -> ApiSession:
+ var urlpath : String = "/v2/account/authenticate/facebookinstantgame"
+ var query_params = ""
+ if p_create != null:
+ query_params += "create=%s&" % str(bool(p_create)).to_lower()
+ if p_username != null:
+ query_params += "username=%s&" % NakamaSerializer.escape_http(p_username)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password)
+ var header = "Basic %s" % credentials
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_account.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiSession.new(result)
+ var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result)
+ return out
+
+ # Authenticate a user with Apple's GameCenter against the server.
+ func authenticate_game_center_async(
+ p_basic_auth_username : String
+ , p_basic_auth_password : String
+ , p_account : ApiAccountGameCenter
+ , p_create = null # : boolean
+ , p_username = null # : string
+ ) -> ApiSession:
+ var urlpath : String = "/v2/account/authenticate/gamecenter"
+ var query_params = ""
+ if p_create != null:
+ query_params += "create=%s&" % str(bool(p_create)).to_lower()
+ if p_username != null:
+ query_params += "username=%s&" % NakamaSerializer.escape_http(p_username)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password)
+ var header = "Basic %s" % credentials
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_account.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiSession.new(result)
+ var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result)
+ return out
+
+ # Authenticate a user with Google against the server.
+ func authenticate_google_async(
+ p_basic_auth_username : String
+ , p_basic_auth_password : String
+ , p_account : ApiAccountGoogle
+ , p_create = null # : boolean
+ , p_username = null # : string
+ ) -> ApiSession:
+ var urlpath : String = "/v2/account/authenticate/google"
+ var query_params = ""
+ if p_create != null:
+ query_params += "create=%s&" % str(bool(p_create)).to_lower()
+ if p_username != null:
+ query_params += "username=%s&" % NakamaSerializer.escape_http(p_username)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password)
+ var header = "Basic %s" % credentials
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_account.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiSession.new(result)
+ var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result)
+ return out
+
+ # Authenticate a user with Steam against the server.
+ func authenticate_steam_async(
+ p_basic_auth_username : String
+ , p_basic_auth_password : String
+ , p_account : ApiAccountSteam
+ , p_create = null # : boolean
+ , p_username = null # : string
+ , p_sync = null # : boolean
+ ) -> ApiSession:
+ var urlpath : String = "/v2/account/authenticate/steam"
+ var query_params = ""
+ if p_create != null:
+ query_params += "create=%s&" % str(bool(p_create)).to_lower()
+ if p_username != null:
+ query_params += "username=%s&" % NakamaSerializer.escape_http(p_username)
+ if p_sync != null:
+ query_params += "sync=%s&" % str(bool(p_sync)).to_lower()
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password)
+ var header = "Basic %s" % credentials
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_account.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiSession.new(result)
+ var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result)
+ return out
+
+ # Add an Apple ID to the social profiles on the current user's account.
+ func link_apple_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountApple
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/link/apple"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Add a custom ID to the social profiles on the current user's account.
+ func link_custom_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountCustom
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/link/custom"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Add a device ID to the social profiles on the current user's account.
+ func link_device_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountDevice
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/link/device"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Add an email+password to the social profiles on the current user's account.
+ func link_email_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountEmail
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/link/email"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Add Facebook to the social profiles on the current user's account.
+ func link_facebook_async(
+ p_session : NakamaSession
+ , p_account : ApiAccountFacebook
+ , p_sync = null # : boolean
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/link/facebook"
+ var query_params = ""
+ if p_sync != null:
+ query_params += "sync=%s&" % str(bool(p_sync)).to_lower()
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_account.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Add Facebook Instant Game to the social profiles on the current user's account.
+ func link_facebook_instant_game_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountFacebookInstantGame
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/link/facebookinstantgame"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Add Apple's GameCenter to the social profiles on the current user's account.
+ func link_game_center_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountGameCenter
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/link/gamecenter"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Add Google to the social profiles on the current user's account.
+ func link_google_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountGoogle
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/link/google"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Add Steam to the social profiles on the current user's account.
+ func link_steam_async(
+ p_session : NakamaSession
+ , p_body : ApiLinkSteamRequest
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/link/steam"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Refresh a user's session using a refresh token retrieved from a previous authentication request.
+ func session_refresh_async(
+ p_basic_auth_username : String
+ , p_basic_auth_password : String
+ , p_body : ApiSessionRefreshRequest
+ ) -> ApiSession:
+ var urlpath : String = "/v2/account/session/refresh"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password)
+ var header = "Basic %s" % credentials
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiSession.new(result)
+ var out : ApiSession = NakamaSerializer.deserialize(_namespace, "ApiSession", result)
+ return out
+
+ # Remove the Apple ID from the social profiles on the current user's account.
+ func unlink_apple_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountApple
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/unlink/apple"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Remove the custom ID from the social profiles on the current user's account.
+ func unlink_custom_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountCustom
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/unlink/custom"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Remove the device ID from the social profiles on the current user's account.
+ func unlink_device_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountDevice
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/unlink/device"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Remove the email+password from the social profiles on the current user's account.
+ func unlink_email_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountEmail
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/unlink/email"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Remove Facebook from the social profiles on the current user's account.
+ func unlink_facebook_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountFacebook
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/unlink/facebook"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Remove Facebook Instant Game profile from the social profiles on the current user's account.
+ func unlink_facebook_instant_game_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountFacebookInstantGame
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/unlink/facebookinstantgame"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Remove Apple's GameCenter from the social profiles on the current user's account.
+ func unlink_game_center_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountGameCenter
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/unlink/gamecenter"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Remove Google from the social profiles on the current user's account.
+ func unlink_google_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountGoogle
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/unlink/google"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Remove Steam from the social profiles on the current user's account.
+ func unlink_steam_async(
+ p_session : NakamaSession
+ , p_body : ApiAccountSteam
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/account/unlink/steam"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # List a channel's message history.
+ func list_channel_messages_async(
+ p_session : NakamaSession
+ , p_channel_id : String
+ , p_limit = null # : integer
+ , p_forward = null # : boolean
+ , p_cursor = null # : string
+ ) -> ApiChannelMessageList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiChannelMessageList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/channel/{channelId}"
+ urlpath = urlpath.replace("{channelId}", NakamaSerializer.escape_http(p_channel_id))
+ var query_params = ""
+ if p_limit != null:
+ query_params += "limit=%d&" % p_limit
+ if p_forward != null:
+ query_params += "forward=%s&" % str(bool(p_forward)).to_lower()
+ if p_cursor != null:
+ query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiChannelMessageList.new(result)
+ var out : ApiChannelMessageList = NakamaSerializer.deserialize(_namespace, "ApiChannelMessageList", result)
+ return out
+
+ # Submit an event for processing in the server's registered runtime custom events handler.
+ func event_async(
+ p_session : NakamaSession
+ , p_body : ApiEvent
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/event"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Delete one or more users by ID or username.
+ func delete_friends_async(
+ p_session : NakamaSession
+ , p_ids = null # : array
+ , p_usernames = null # : array
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/friend"
+ var query_params = ""
+ if p_ids != null:
+ for elem in p_ids:
+ query_params += "ids=%s&" % elem
+ if p_usernames != null:
+ for elem in p_usernames:
+ query_params += "usernames=%s&" % elem
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "DELETE"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # List all friends for the current user.
+ func list_friends_async(
+ p_session : NakamaSession
+ , p_limit = null # : integer
+ , p_state = null # : integer
+ , p_cursor = null # : string
+ ) -> ApiFriendList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiFriendList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/friend"
+ var query_params = ""
+ if p_limit != null:
+ query_params += "limit=%d&" % p_limit
+ if p_state != null:
+ query_params += "state=%d&" % p_state
+ if p_cursor != null:
+ query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiFriendList.new(result)
+ var out : ApiFriendList = NakamaSerializer.deserialize(_namespace, "ApiFriendList", result)
+ return out
+
+ # Add friends by ID or username to a user's account.
+ func add_friends_async(
+ p_session : NakamaSession
+ , p_ids = null # : array
+ , p_usernames = null # : array
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/friend"
+ var query_params = ""
+ if p_ids != null:
+ for elem in p_ids:
+ query_params += "ids=%s&" % elem
+ if p_usernames != null:
+ for elem in p_usernames:
+ query_params += "usernames=%s&" % elem
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Block one or more users by ID or username.
+ func block_friends_async(
+ p_session : NakamaSession
+ , p_ids = null # : array
+ , p_usernames = null # : array
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/friend/block"
+ var query_params = ""
+ if p_ids != null:
+ for elem in p_ids:
+ query_params += "ids=%s&" % elem
+ if p_usernames != null:
+ for elem in p_usernames:
+ query_params += "usernames=%s&" % elem
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Import Facebook friends and add them to a user's account.
+ func import_facebook_friends_async(
+ p_session : NakamaSession
+ , p_account : ApiAccountFacebook
+ , p_reset = null # : boolean
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/friend/facebook"
+ var query_params = ""
+ if p_reset != null:
+ query_params += "reset=%s&" % str(bool(p_reset)).to_lower()
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_account.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Import Steam friends and add them to a user's account.
+ func import_steam_friends_async(
+ p_session : NakamaSession
+ , p_account : ApiAccountSteam
+ , p_reset = null # : boolean
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/friend/steam"
+ var query_params = ""
+ if p_reset != null:
+ query_params += "reset=%s&" % str(bool(p_reset)).to_lower()
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_account.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # List groups based on given filters.
+ func list_groups_async(
+ p_session : NakamaSession
+ , p_name = null # : string
+ , p_cursor = null # : string
+ , p_limit = null # : integer
+ , p_lang_tag = null # : string
+ , p_members = null # : integer
+ , p_open = null # : boolean
+ ) -> ApiGroupList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiGroupList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/group"
+ var query_params = ""
+ if p_name != null:
+ query_params += "name=%s&" % NakamaSerializer.escape_http(p_name)
+ if p_cursor != null:
+ query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor)
+ if p_limit != null:
+ query_params += "limit=%d&" % p_limit
+ if p_lang_tag != null:
+ query_params += "lang_tag=%s&" % NakamaSerializer.escape_http(p_lang_tag)
+ if p_members != null:
+ query_params += "members=%d&" % p_members
+ if p_open != null:
+ query_params += "open=%s&" % str(bool(p_open)).to_lower()
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiGroupList.new(result)
+ var out : ApiGroupList = NakamaSerializer.deserialize(_namespace, "ApiGroupList", result)
+ return out
+
+ # Create a new group with the current user as the owner.
+ func create_group_async(
+ p_session : NakamaSession
+ , p_body : ApiCreateGroupRequest
+ ) -> ApiGroup:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiGroup.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/group"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiGroup.new(result)
+ var out : ApiGroup = NakamaSerializer.deserialize(_namespace, "ApiGroup", result)
+ return out
+
+ # Delete a group by ID.
+ func delete_group_async(
+ p_session : NakamaSession
+ , p_group_id : String
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/group/{groupId}"
+ urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id))
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "DELETE"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Update fields in a given group.
+ func update_group_async(
+ p_session : NakamaSession
+ , p_group_id : String
+ , p_body : ApiUpdateGroupRequest
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/group/{groupId}"
+ urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id))
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "PUT"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Add users to a group.
+ func add_group_users_async(
+ p_session : NakamaSession
+ , p_group_id : String
+ , p_user_ids = null # : array
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/group/{groupId}/add"
+ urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id))
+ var query_params = ""
+ if p_user_ids != null:
+ for elem in p_user_ids:
+ query_params += "user_ids=%s&" % elem
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Ban a set of users from a group.
+ func ban_group_users_async(
+ p_session : NakamaSession
+ , p_group_id : String
+ , p_user_ids = null # : array
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/group/{groupId}/ban"
+ urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id))
+ var query_params = ""
+ if p_user_ids != null:
+ for elem in p_user_ids:
+ query_params += "user_ids=%s&" % elem
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Demote a set of users in a group to the next role down.
+ func demote_group_users_async(
+ p_session : NakamaSession
+ , p_group_id : String
+ , p_user_ids = null # : array
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/group/{groupId}/demote"
+ urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id))
+ var query_params = ""
+ if p_user_ids != null:
+ for elem in p_user_ids:
+ query_params += "user_ids=%s&" % elem
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Immediately join an open group, or request to join a closed one.
+ func join_group_async(
+ p_session : NakamaSession
+ , p_group_id : String
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/group/{groupId}/join"
+ urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id))
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Kick a set of users from a group.
+ func kick_group_users_async(
+ p_session : NakamaSession
+ , p_group_id : String
+ , p_user_ids = null # : array
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/group/{groupId}/kick"
+ urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id))
+ var query_params = ""
+ if p_user_ids != null:
+ for elem in p_user_ids:
+ query_params += "user_ids=%s&" % elem
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Leave a group the user is a member of.
+ func leave_group_async(
+ p_session : NakamaSession
+ , p_group_id : String
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/group/{groupId}/leave"
+ urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id))
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Promote a set of users in a group to the next role up.
+ func promote_group_users_async(
+ p_session : NakamaSession
+ , p_group_id : String
+ , p_user_ids = null # : array
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/group/{groupId}/promote"
+ urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id))
+ var query_params = ""
+ if p_user_ids != null:
+ for elem in p_user_ids:
+ query_params += "user_ids=%s&" % elem
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # List all users that are part of a group.
+ func list_group_users_async(
+ p_session : NakamaSession
+ , p_group_id : String
+ , p_limit = null # : integer
+ , p_state = null # : integer
+ , p_cursor = null # : string
+ ) -> ApiGroupUserList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiGroupUserList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/group/{groupId}/user"
+ urlpath = urlpath.replace("{groupId}", NakamaSerializer.escape_http(p_group_id))
+ var query_params = ""
+ if p_limit != null:
+ query_params += "limit=%d&" % p_limit
+ if p_state != null:
+ query_params += "state=%d&" % p_state
+ if p_cursor != null:
+ query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiGroupUserList.new(result)
+ var out : ApiGroupUserList = NakamaSerializer.deserialize(_namespace, "ApiGroupUserList", result)
+ return out
+
+ # Validate Apple IAP Receipt
+ func validate_purchase_apple_async(
+ p_session : NakamaSession
+ , p_body : ApiValidatePurchaseAppleRequest
+ ) -> ApiValidatePurchaseResponse:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiValidatePurchaseResponse.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/iap/purchase/apple"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiValidatePurchaseResponse.new(result)
+ var out : ApiValidatePurchaseResponse = NakamaSerializer.deserialize(_namespace, "ApiValidatePurchaseResponse", result)
+ return out
+
+ # Validate Google IAP Receipt
+ func validate_purchase_google_async(
+ p_session : NakamaSession
+ , p_body : ApiValidatePurchaseGoogleRequest
+ ) -> ApiValidatePurchaseResponse:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiValidatePurchaseResponse.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/iap/purchase/google"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiValidatePurchaseResponse.new(result)
+ var out : ApiValidatePurchaseResponse = NakamaSerializer.deserialize(_namespace, "ApiValidatePurchaseResponse", result)
+ return out
+
+ # Validate Huawei IAP Receipt
+ func validate_purchase_huawei_async(
+ p_session : NakamaSession
+ , p_body : ApiValidatePurchaseHuaweiRequest
+ ) -> ApiValidatePurchaseResponse:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiValidatePurchaseResponse.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/iap/purchase/huawei"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiValidatePurchaseResponse.new(result)
+ var out : ApiValidatePurchaseResponse = NakamaSerializer.deserialize(_namespace, "ApiValidatePurchaseResponse", result)
+ return out
+
+ # List user's subscriptions.
+ func list_subscriptions_async(
+ p_session : NakamaSession
+ , p_body : ApiListSubscriptionsRequest
+ ) -> ApiSubscriptionList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiSubscriptionList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/iap/subscription"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiSubscriptionList.new(result)
+ var out : ApiSubscriptionList = NakamaSerializer.deserialize(_namespace, "ApiSubscriptionList", result)
+ return out
+
+ # Validate Apple Subscription Receipt
+ func validate_subscription_apple_async(
+ p_session : NakamaSession
+ , p_body : ApiValidateSubscriptionAppleRequest
+ ) -> ApiValidateSubscriptionResponse:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiValidateSubscriptionResponse.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/iap/subscription/apple"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiValidateSubscriptionResponse.new(result)
+ var out : ApiValidateSubscriptionResponse = NakamaSerializer.deserialize(_namespace, "ApiValidateSubscriptionResponse", result)
+ return out
+
+ # Validate Google Subscription Receipt
+ func validate_subscription_google_async(
+ p_session : NakamaSession
+ , p_body : ApiValidateSubscriptionGoogleRequest
+ ) -> ApiValidateSubscriptionResponse:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiValidateSubscriptionResponse.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/iap/subscription/google"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiValidateSubscriptionResponse.new(result)
+ var out : ApiValidateSubscriptionResponse = NakamaSerializer.deserialize(_namespace, "ApiValidateSubscriptionResponse", result)
+ return out
+
+ # Get subscription by product id.
+ func get_subscription_async(
+ p_session : NakamaSession
+ , p_product_id : String
+ ) -> ApiValidatedSubscription:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiValidatedSubscription.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/iap/subscription/{productId}"
+ urlpath = urlpath.replace("{productId}", NakamaSerializer.escape_http(p_product_id))
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiValidatedSubscription.new(result)
+ var out : ApiValidatedSubscription = NakamaSerializer.deserialize(_namespace, "ApiValidatedSubscription", result)
+ return out
+
+ # Delete a leaderboard record.
+ func delete_leaderboard_record_async(
+ p_session : NakamaSession
+ , p_leaderboard_id : String
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/leaderboard/{leaderboardId}"
+ urlpath = urlpath.replace("{leaderboardId}", NakamaSerializer.escape_http(p_leaderboard_id))
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "DELETE"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # List leaderboard records.
+ func list_leaderboard_records_async(
+ p_session : NakamaSession
+ , p_leaderboard_id : String
+ , p_owner_ids = null # : array
+ , p_limit = null # : integer
+ , p_cursor = null # : string
+ , p_expiry = null # : string
+ ) -> ApiLeaderboardRecordList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiLeaderboardRecordList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/leaderboard/{leaderboardId}"
+ urlpath = urlpath.replace("{leaderboardId}", NakamaSerializer.escape_http(p_leaderboard_id))
+ var query_params = ""
+ if p_owner_ids != null:
+ for elem in p_owner_ids:
+ query_params += "owner_ids=%s&" % elem
+ if p_limit != null:
+ query_params += "limit=%d&" % p_limit
+ if p_cursor != null:
+ query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor)
+ if p_expiry != null:
+ query_params += "expiry=%s&" % NakamaSerializer.escape_http(p_expiry)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiLeaderboardRecordList.new(result)
+ var out : ApiLeaderboardRecordList = NakamaSerializer.deserialize(_namespace, "ApiLeaderboardRecordList", result)
+ return out
+
+ # Write a record to a leaderboard.
+ func write_leaderboard_record_async(
+ p_session : NakamaSession
+ , p_leaderboard_id : String
+ , p_record : WriteLeaderboardRecordRequestLeaderboardRecordWrite
+ ) -> ApiLeaderboardRecord:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiLeaderboardRecord.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/leaderboard/{leaderboardId}"
+ urlpath = urlpath.replace("{leaderboardId}", NakamaSerializer.escape_http(p_leaderboard_id))
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_record.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiLeaderboardRecord.new(result)
+ var out : ApiLeaderboardRecord = NakamaSerializer.deserialize(_namespace, "ApiLeaderboardRecord", result)
+ return out
+
+ # List leaderboard records that belong to a user.
+ func list_leaderboard_records_around_owner_async(
+ p_session : NakamaSession
+ , p_leaderboard_id : String
+ , p_owner_id : String
+ , p_limit = null # : integer
+ , p_expiry = null # : string
+ , p_cursor = null # : string
+ ) -> ApiLeaderboardRecordList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiLeaderboardRecordList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/leaderboard/{leaderboardId}/owner/{ownerId}"
+ urlpath = urlpath.replace("{leaderboardId}", NakamaSerializer.escape_http(p_leaderboard_id))
+ urlpath = urlpath.replace("{ownerId}", NakamaSerializer.escape_http(p_owner_id))
+ var query_params = ""
+ if p_limit != null:
+ query_params += "limit=%d&" % p_limit
+ if p_expiry != null:
+ query_params += "expiry=%s&" % NakamaSerializer.escape_http(p_expiry)
+ if p_cursor != null:
+ query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiLeaderboardRecordList.new(result)
+ var out : ApiLeaderboardRecordList = NakamaSerializer.deserialize(_namespace, "ApiLeaderboardRecordList", result)
+ return out
+
+ # Fetch list of running matches.
+ func list_matches_async(
+ p_session : NakamaSession
+ , p_limit = null # : integer
+ , p_authoritative = null # : boolean
+ , p_label = null # : string
+ , p_min_size = null # : integer
+ , p_max_size = null # : integer
+ , p_query = null # : string
+ ) -> ApiMatchList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiMatchList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/match"
+ var query_params = ""
+ if p_limit != null:
+ query_params += "limit=%d&" % p_limit
+ if p_authoritative != null:
+ query_params += "authoritative=%s&" % str(bool(p_authoritative)).to_lower()
+ if p_label != null:
+ query_params += "label=%s&" % NakamaSerializer.escape_http(p_label)
+ if p_min_size != null:
+ query_params += "min_size=%d&" % p_min_size
+ if p_max_size != null:
+ query_params += "max_size=%d&" % p_max_size
+ if p_query != null:
+ query_params += "query=%s&" % NakamaSerializer.escape_http(p_query)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiMatchList.new(result)
+ var out : ApiMatchList = NakamaSerializer.deserialize(_namespace, "ApiMatchList", result)
+ return out
+
+ # Delete one or more notifications for the current user.
+ func delete_notifications_async(
+ p_session : NakamaSession
+ , p_ids = null # : array
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/notification"
+ var query_params = ""
+ if p_ids != null:
+ for elem in p_ids:
+ query_params += "ids=%s&" % elem
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "DELETE"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Fetch list of notifications.
+ func list_notifications_async(
+ p_session : NakamaSession
+ , p_limit = null # : integer
+ , p_cacheable_cursor = null # : string
+ ) -> ApiNotificationList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiNotificationList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/notification"
+ var query_params = ""
+ if p_limit != null:
+ query_params += "limit=%d&" % p_limit
+ if p_cacheable_cursor != null:
+ query_params += "cacheable_cursor=%s&" % NakamaSerializer.escape_http(p_cacheable_cursor)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiNotificationList.new(result)
+ var out : ApiNotificationList = NakamaSerializer.deserialize(_namespace, "ApiNotificationList", result)
+ return out
+
+ # Execute a Lua function on the server.
+ func rpc_func2_async(
+ p_bearer_token : String
+ , p_id : String
+ , p_payload = null # : string
+ , p_http_key = null # : string
+ ) -> ApiRpc:
+ var urlpath : String = "/v2/rpc/{id}"
+ urlpath = urlpath.replace("{id}", NakamaSerializer.escape_http(p_id))
+ var query_params = ""
+ if p_payload != null:
+ query_params += "payload=%s&" % NakamaSerializer.escape_http(p_payload)
+ if p_http_key != null:
+ query_params += "http_key=%s&" % NakamaSerializer.escape_http(p_http_key)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ if (p_bearer_token):
+ var header = "Bearer %s" % p_bearer_token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiRpc.new(result)
+ var out : ApiRpc = NakamaSerializer.deserialize(_namespace, "ApiRpc", result)
+ return out
+
+ # Execute a Lua function on the server.
+ func rpc_func_async(
+ p_bearer_token : String
+ , p_id : String
+ , p_payload : String
+ , p_http_key = null # : string
+ ) -> ApiRpc:
+ var urlpath : String = "/v2/rpc/{id}"
+ urlpath = urlpath.replace("{id}", NakamaSerializer.escape_http(p_id))
+ var query_params = ""
+ if p_http_key != null:
+ query_params += "http_key=%s&" % NakamaSerializer.escape_http(p_http_key)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ if (p_bearer_token):
+ var header = "Bearer %s" % p_bearer_token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_payload).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiRpc.new(result)
+ var out : ApiRpc = NakamaSerializer.deserialize(_namespace, "ApiRpc", result)
+ return out
+
+ # Log out a session, invalidate a refresh token, or log out all sessions/refresh tokens for a user.
+ func session_logout_async(
+ p_session : NakamaSession
+ , p_body : ApiSessionLogoutRequest
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/session/logout"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # Get storage objects.
+ func read_storage_objects_async(
+ p_session : NakamaSession
+ , p_body : ApiReadStorageObjectsRequest
+ ) -> ApiStorageObjects:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiStorageObjects.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/storage"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiStorageObjects.new(result)
+ var out : ApiStorageObjects = NakamaSerializer.deserialize(_namespace, "ApiStorageObjects", result)
+ return out
+
+ # Write objects into the storage engine.
+ func write_storage_objects_async(
+ p_session : NakamaSession
+ , p_body : ApiWriteStorageObjectsRequest
+ ) -> ApiStorageObjectAcks:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiStorageObjectAcks.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/storage"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "PUT"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiStorageObjectAcks.new(result)
+ var out : ApiStorageObjectAcks = NakamaSerializer.deserialize(_namespace, "ApiStorageObjectAcks", result)
+ return out
+
+ # Delete one or more objects by ID or username.
+ func delete_storage_objects_async(
+ p_session : NakamaSession
+ , p_body : ApiDeleteStorageObjectsRequest
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/storage/delete"
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "PUT"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_body.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # List publicly readable storage objects in a given collection.
+ func list_storage_objects_async(
+ p_session : NakamaSession
+ , p_collection : String
+ , p_user_id = null # : string
+ , p_limit = null # : integer
+ , p_cursor = null # : string
+ ) -> ApiStorageObjectList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiStorageObjectList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/storage/{collection}"
+ urlpath = urlpath.replace("{collection}", NakamaSerializer.escape_http(p_collection))
+ var query_params = ""
+ if p_user_id != null:
+ query_params += "user_id=%s&" % NakamaSerializer.escape_http(p_user_id)
+ if p_limit != null:
+ query_params += "limit=%d&" % p_limit
+ if p_cursor != null:
+ query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiStorageObjectList.new(result)
+ var out : ApiStorageObjectList = NakamaSerializer.deserialize(_namespace, "ApiStorageObjectList", result)
+ return out
+
+ # List publicly readable storage objects in a given collection.
+ func list_storage_objects2_async(
+ p_session : NakamaSession
+ , p_collection : String
+ , p_user_id : String
+ , p_limit = null # : integer
+ , p_cursor = null # : string
+ ) -> ApiStorageObjectList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiStorageObjectList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/storage/{collection}/{userId}"
+ urlpath = urlpath.replace("{collection}", NakamaSerializer.escape_http(p_collection))
+ urlpath = urlpath.replace("{userId}", NakamaSerializer.escape_http(p_user_id))
+ var query_params = ""
+ if p_limit != null:
+ query_params += "limit=%d&" % p_limit
+ if p_cursor != null:
+ query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiStorageObjectList.new(result)
+ var out : ApiStorageObjectList = NakamaSerializer.deserialize(_namespace, "ApiStorageObjectList", result)
+ return out
+
+ # List current or upcoming tournaments.
+ func list_tournaments_async(
+ p_session : NakamaSession
+ , p_category_start = null # : integer
+ , p_category_end = null # : integer
+ , p_start_time = null # : integer
+ , p_end_time = null # : integer
+ , p_limit = null # : integer
+ , p_cursor = null # : string
+ ) -> ApiTournamentList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiTournamentList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/tournament"
+ var query_params = ""
+ if p_category_start != null:
+ query_params += "category_start=%d&" % p_category_start
+ if p_category_end != null:
+ query_params += "category_end=%d&" % p_category_end
+ if p_start_time != null:
+ query_params += "start_time=%d&" % p_start_time
+ if p_end_time != null:
+ query_params += "end_time=%d&" % p_end_time
+ if p_limit != null:
+ query_params += "limit=%d&" % p_limit
+ if p_cursor != null:
+ query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiTournamentList.new(result)
+ var out : ApiTournamentList = NakamaSerializer.deserialize(_namespace, "ApiTournamentList", result)
+ return out
+
+ # List tournament records.
+ func list_tournament_records_async(
+ p_session : NakamaSession
+ , p_tournament_id : String
+ , p_owner_ids = null # : array
+ , p_limit = null # : integer
+ , p_cursor = null # : string
+ , p_expiry = null # : string
+ ) -> ApiTournamentRecordList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiTournamentRecordList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/tournament/{tournamentId}"
+ urlpath = urlpath.replace("{tournamentId}", NakamaSerializer.escape_http(p_tournament_id))
+ var query_params = ""
+ if p_owner_ids != null:
+ for elem in p_owner_ids:
+ query_params += "owner_ids=%s&" % elem
+ if p_limit != null:
+ query_params += "limit=%d&" % p_limit
+ if p_cursor != null:
+ query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor)
+ if p_expiry != null:
+ query_params += "expiry=%s&" % NakamaSerializer.escape_http(p_expiry)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiTournamentRecordList.new(result)
+ var out : ApiTournamentRecordList = NakamaSerializer.deserialize(_namespace, "ApiTournamentRecordList", result)
+ return out
+
+ # Write a record to a tournament.
+ func write_tournament_record2_async(
+ p_session : NakamaSession
+ , p_tournament_id : String
+ , p_record : WriteTournamentRecordRequestTournamentRecordWrite
+ ) -> ApiLeaderboardRecord:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiLeaderboardRecord.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/tournament/{tournamentId}"
+ urlpath = urlpath.replace("{tournamentId}", NakamaSerializer.escape_http(p_tournament_id))
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_record.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiLeaderboardRecord.new(result)
+ var out : ApiLeaderboardRecord = NakamaSerializer.deserialize(_namespace, "ApiLeaderboardRecord", result)
+ return out
+
+ # Write a record to a tournament.
+ func write_tournament_record_async(
+ p_session : NakamaSession
+ , p_tournament_id : String
+ , p_record : WriteTournamentRecordRequestTournamentRecordWrite
+ ) -> ApiLeaderboardRecord:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiLeaderboardRecord.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/tournament/{tournamentId}"
+ urlpath = urlpath.replace("{tournamentId}", NakamaSerializer.escape_http(p_tournament_id))
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "PUT"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+ content = JSON.stringify(p_record.serialize()).to_utf8_buffer()
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiLeaderboardRecord.new(result)
+ var out : ApiLeaderboardRecord = NakamaSerializer.deserialize(_namespace, "ApiLeaderboardRecord", result)
+ return out
+
+ # Attempt to join an open and running tournament.
+ func join_tournament_async(
+ p_session : NakamaSession
+ , p_tournament_id : String
+ ) -> NakamaAsyncResult:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return NakamaAsyncResult.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/tournament/{tournamentId}/join"
+ urlpath = urlpath.replace("{tournamentId}", NakamaSerializer.escape_http(p_tournament_id))
+ var query_params = ""
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "POST"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return NakamaAsyncResult.new(result)
+ return NakamaAsyncResult.new()
+
+ # List tournament records for a given owner.
+ func list_tournament_records_around_owner_async(
+ p_session : NakamaSession
+ , p_tournament_id : String
+ , p_owner_id : String
+ , p_limit = null # : integer
+ , p_expiry = null # : string
+ , p_cursor = null # : string
+ ) -> ApiTournamentRecordList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiTournamentRecordList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/tournament/{tournamentId}/owner/{ownerId}"
+ urlpath = urlpath.replace("{tournamentId}", NakamaSerializer.escape_http(p_tournament_id))
+ urlpath = urlpath.replace("{ownerId}", NakamaSerializer.escape_http(p_owner_id))
+ var query_params = ""
+ if p_limit != null:
+ query_params += "limit=%d&" % p_limit
+ if p_expiry != null:
+ query_params += "expiry=%s&" % NakamaSerializer.escape_http(p_expiry)
+ if p_cursor != null:
+ query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiTournamentRecordList.new(result)
+ var out : ApiTournamentRecordList = NakamaSerializer.deserialize(_namespace, "ApiTournamentRecordList", result)
+ return out
+
+ # Fetch zero or more users by ID and/or username.
+ func get_users_async(
+ p_session : NakamaSession
+ , p_ids = null # : array
+ , p_usernames = null # : array
+ , p_facebook_ids = null # : array
+ ) -> ApiUsers:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiUsers.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/user"
+ var query_params = ""
+ if p_ids != null:
+ for elem in p_ids:
+ query_params += "ids=%s&" % elem
+ if p_usernames != null:
+ for elem in p_usernames:
+ query_params += "usernames=%s&" % elem
+ if p_facebook_ids != null:
+ for elem in p_facebook_ids:
+ query_params += "facebook_ids=%s&" % elem
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiUsers.new(result)
+ var out : ApiUsers = NakamaSerializer.deserialize(_namespace, "ApiUsers", result)
+ return out
+
+ # List groups the current user belongs to.
+ func list_user_groups_async(
+ p_session : NakamaSession
+ , p_user_id : String
+ , p_limit = null # : integer
+ , p_state = null # : integer
+ , p_cursor = null # : string
+ ) -> ApiUserGroupList:
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return ApiUserGroupList.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ var urlpath : String = "/v2/user/{userId}/group"
+ urlpath = urlpath.replace("{userId}", NakamaSerializer.escape_http(p_user_id))
+ var query_params = ""
+ if p_limit != null:
+ query_params += "limit=%d&" % p_limit
+ if p_state != null:
+ query_params += "state=%d&" % p_state
+ if p_cursor != null:
+ query_params += "cursor=%s&" % NakamaSerializer.escape_http(p_cursor)
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "GET"
+ var headers = {}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+
+ var content : PackedByteArray
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return ApiUserGroupList.new(result)
+ var out : ApiUserGroupList = NakamaSerializer.deserialize(_namespace, "ApiUserGroupList", result)
+ return out
diff --git a/game/addons/com.heroiclabs.nakama/api/NakamaAPI.gd.uid b/game/addons/com.heroiclabs.nakama/api/NakamaAPI.gd.uid
new file mode 100644
index 0000000..9125d04
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/api/NakamaAPI.gd.uid
@@ -0,0 +1 @@
+uid://cosu38ardyfeo
diff --git a/game/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd b/game/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd
new file mode 100644
index 0000000..05fc5ae
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd
@@ -0,0 +1,902 @@
+extends NakamaAsyncResult
+
+class_name NakamaRTAPI
+
+# A chat channel on the server.
+class Channel extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "id": {"name": "id", "type": TYPE_STRING, "required": true},
+ "presences": {"name": "presences", "type": TYPE_ARRAY, "required": false, "content": "UserPresence"},
+ "self": {"name": "self_presence", "type": "UserPresence", "required": true},
+ "room_name": {"name": "room_name", "type": TYPE_STRING, "required": false},
+ "group_id": {"name": "group_id", "type": TYPE_STRING, "required": false},
+ "user_id_one": {"name": "user_id_one", "type": TYPE_STRING, "required": false},
+ "user_id_two": {"name": "user_id_two", "type": TYPE_STRING, "required": false}
+ }
+
+ # The server-assigned channel ID.
+ var id : String
+
+ # The presences visible on the chat channel.
+ var presences : Array # of objects NakamaUserPresence
+
+ # The presence of the current user. i.e. Your self.
+ var self_presence : NakamaRTAPI.UserPresence
+
+ # The name of the chat room, or an empty string if this message was not sent through a chat room.
+ var room_name : String
+
+ # The ID of the group, or an empty string if this message was not sent through a group channel.
+ var group_id : String
+
+ # The ID of the first DM user, or an empty string if this message was not sent through a DM chat.
+ var user_id_one : String
+
+ # The ID of the second DM user, or an empty string if this message was not sent through a DM chat.
+ var user_id_two : String
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "Channel<id=%s, presences=%s, self=%s, room_name=%s, group_id=%s, user_id_one=%s, user_id_two=%s>" % [
+ id, presences, self_presence, room_name, group_id, user_id_one, user_id_two
+ ]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> Channel:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "Channel", p_dict), Channel) as Channel
+
+ static func get_result_key() -> String:
+ return "channel"
+
+
+class ChannelMessageAck extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "channel_id": {"name": "channel_id", "type": TYPE_STRING, "required": true},
+ "code": {"name": "code", "type": TYPE_INT, "required": true},
+ "create_time": {"name": "create_time", "type": TYPE_STRING, "required": false},
+ "message_id": {"name": "message_id", "type": TYPE_STRING, "required": true},
+ "persistent": {"name": "persistent", "type": TYPE_BOOL, "required": false},
+ "update_time": {"name": "update_time", "type": TYPE_STRING, "required": false},
+ "username": {"name": "username", "type": TYPE_STRING, "required": false},
+ "room_name": {"name": "room_name", "type": TYPE_STRING, "required": false},
+ "group_id": {"name": "group_id", "type": TYPE_STRING, "required": false},
+ "user_id_one": {"name": "user_id_one", "type": TYPE_STRING, "required": false},
+ "user_id_two": {"name": "user_id_two", "type": TYPE_STRING, "required": false}
+ }
+
+ # The server-assigned channel ID.
+ var channel_id : String
+
+ # A user-defined code for the chat message.
+ var code : int
+
+ # The UNIX time when the message was created.
+ var create_time : String
+
+ # A unique ID for the chat message.
+ var message_id : String
+
+ # True if the chat message has been stored in history.
+ var persistent : bool
+
+ # The UNIX time when the message was updated.
+ var update_time : String
+
+ # The username of the sender of the message.
+ var username : String
+
+ # The name of the chat room, or an empty string if this message was not sent through a chat room.
+ var room_name : String
+
+ # The ID of the group, or an empty string if this message was not sent through a group channel.
+ var group_id : String
+
+ # The ID of the first DM user, or an empty string if this message was not sent through a DM chat.
+ var user_id_one : String
+
+ # The ID of the second DM user, or an empty string if this message was not sent through a DM chat.
+ var user_id_two : String
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "ChannelMessageAck<channel_id=%s, code=%d, create_time=%s, message_id=%s, persistent=%s, update_time=%s, username=%s room_name=%s, group_id=%s, user_id_one=%s, user_id_two=%s>" % [
+ channel_id, code, create_time, message_id, persistent, update_time, username, room_name, group_id, user_id_one, user_id_two
+ ]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ChannelMessageAck:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ChannelMessageAck", p_dict), ChannelMessageAck) as ChannelMessageAck
+
+ static func get_result_key() -> String:
+ return "channel_message_ack"
+
+
+# A batch of join and leave presences on a chat channel.
+class ChannelPresenceEvent extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "channel_id": {"name": "channel_id", "type": TYPE_STRING, "required": true},
+ "joins": {"name": "joins", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
+ "leaves": {"name": "leaves", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
+ "room_name": {"name": "room_name", "type": TYPE_STRING, "required": false},
+ "group_id": {"name": "group_id", "type": TYPE_STRING, "required": false},
+ "user_id_one": {"name": "user_id_one", "type": TYPE_STRING, "required": false},
+ "user_id_two": {"name": "user_id_two", "type": TYPE_STRING, "required": false}
+ }
+
+ # The unique identifier of the chat channel.
+ var channel_id : String
+
+ # Presences of the users who joined the channel.
+ var joins : Array # UserPresence
+
+ # Presences of users who left the channel.
+ var leaves : Array # UserPresence
+
+ # The name of the chat room, or an empty string if this message was not sent through a chat room.
+ var room_name : String
+
+ # The ID of the group, or an empty string if this message was not sent through a group channel.
+ var group_id : String
+
+ # The ID of the first DM user, or an empty string if this message was not sent through a DM chat.
+ var user_id_one : String
+
+ # The ID of the second DM user, or an empty string if this message was not sent through a DM chat.
+ var user_id_two : String
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "ChannelPresenceEvent<channel_id=%s, joins=%s, leaves=%s, room_name=%s, group_id=%s, user_id_one=%s, user_id_two=%s>" % [
+ channel_id, joins, leaves, room_name, group_id, user_id_one, user_id_two
+ ]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> ChannelPresenceEvent:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "ChannelPresenceEvent", p_dict), ChannelPresenceEvent) as ChannelPresenceEvent
+
+ static func get_result_key() -> String:
+ return "channel_presence_event"
+
+
+# Describes an error which occurred on the server.
+class Error extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "code": {"name": "code", "type": TYPE_INT, "required": true},
+ "message": {"name": "message", "type": TYPE_STRING, "required": true},
+ "context": {"name": "context", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ }
+
+ # The selection of possible error codes.
+ enum Code {
+ # An unexpected result from the server.
+ RUNTIME_EXCEPTION = 0,
+ # The server received a message which is not recognised.
+ UNRECOGNIZED_PAYLOAD = 1,
+ # A message was expected but contains no content.
+ MISSING_PAYLOAD = 2,
+ # Fields in the message have an invalid format.
+ BAD_INPUT = 3,
+ # The match id was not found.
+ MATCH_NOT_FOUND = 4,
+ # The match join was rejected.
+ MATCH_JOIN_REJECTED = 5,
+ # The runtime function does not exist on the server.
+ RUNTIME_FUNCTION_NOT_FOUND = 6,
+ #The runtime function executed with an error.
+ RUNTIME_FUNCTION_EXCEPTION = 7,
+ }
+
+ # The error code which should be one of "Error.Code" enums.
+ var code : int
+
+ # A message in English to help developers debug the response.
+ var message : String
+
+ # Additional error details which may be different for each response.
+ var context : Dictionary
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "Error<code=%s, messages=%s, context=%s>" % [code, message, context]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> Error:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "Error", p_dict), Error) as Error
+
+ static func get_result_key() -> String:
+ return "error"
+
+
+# A multiplayer match.
+class Match extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "authoritative": {"name": "authoritative", "type": TYPE_BOOL, "required": false},
+ "match_id": {"name": "match_id", "type": TYPE_STRING, "required": true},
+ "label": {"name": "label", "type": TYPE_STRING, "required": false},
+ "presences": {"name": "presences", "type": TYPE_ARRAY, "required": false, "content": "UserPresence"},
+ "size": {"name": "size", "type": TYPE_INT, "required": false},
+ "self": {"name": "self_user", "type": "UserPresence", "required": true}
+ }
+
+ # If this match has an authoritative handler on the server.
+ var authoritative : bool
+
+ # The unique match identifier.
+ var match_id : String
+
+ # A label for the match which can be filtered on.
+ var label : String
+
+ # The presences already in the match.
+ var presences : Array # UserPresence
+
+ # The number of users currently in the match.
+ var size : int
+
+ # The current user in this match. i.e. Yourself.
+ var self_user : UserPresence
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary):
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "Match", p_dict), Match) as Match
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "Match<authoritative=%s, match_id=%s, label=%s, presences=%s, size=%d, self=%s>" % [authoritative, match_id, label, presences, size, self_user]
+
+ static func get_result_key() -> String:
+ return "match"
+
+
+# Some game state update in a match.
+class MatchData extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "match_id": {"name": "match_id", "type": TYPE_STRING, "required": true},
+ "presence": {"name": "presence", "type": "UserPresence", "required": false},
+ "op_code": {"name": "op_code", "type": TYPE_INT, "required": false},
+ "data": {"name": "data", "type": TYPE_STRING, "required": false}
+ }
+
+ # The unique match identifier.
+ var match_id : String
+
+ # The operation code for the state change.
+ # This value can be used to mark the type of the contents of the state.
+ var op_code : int = 0
+
+ # The user that sent this game state update.
+ var presence : UserPresence
+
+ # The raw base64-encoded contents of the state change.
+ var base64_data : String
+
+ # The contents of the state change decoded as a UTF-8 string.
+ var _data
+ var data : String:
+ get:
+ if _data == null and base64_data != '':
+ _data = Marshalls.base64_to_utf8(base64_data)
+ return _data if _data != null else ''
+ set(v):
+ _data = v
+
+ # The contents of the state change decoded as binary data.
+ var _binary_data
+ var binary_data : PackedByteArray:
+ get:
+ if _binary_data == null and base64_data != '':
+ _binary_data = Marshalls.base64_to_raw(base64_data)
+ return _binary_data
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "MatchData<match_id=%s, op_code=%s, presence=%s, data=%s>" % [match_id, op_code, presence, data]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchData:
+ var out = _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchData", p_dict), MatchData) as MatchData
+ # Store the base64 data, ready to be decoded when the developer requests it.
+ if out._data != null:
+ out.base64_data = out._data
+ out._data = null
+ return out
+
+ static func get_result_key() -> String:
+ return "match_data"
+
+
+# A batch of join and leave presences for a match.
+class MatchPresenceEvent extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "match_id": {"name": "match_id", "type": TYPE_STRING, "required": true},
+ "joins": {"name": "joins", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
+ "leaves": {"name": "leaves", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
+ }
+
+ # Presences of users who joined the match.
+ var joins : Array
+
+ # Presences of users who left the match.
+ var leaves : Array
+
+ # The unique match identifier.
+ var match_id : String
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "MatchPresenceEvent<match_id=%s, joins=%s, leaves=%s>" % [match_id, joins, leaves]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchPresenceEvent:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchPresenceEvent", p_dict), MatchPresenceEvent) as MatchPresenceEvent
+
+ static func get_result_key() -> String:
+ return "match_presence_event"
+
+
+# The result of a successful matchmaker operation sent to the server.
+class MatchmakerMatched extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "match_id": {"name": "match_id", "type": TYPE_STRING, "required": false},
+ "ticket": {"name": "ticket", "type": TYPE_STRING, "required": true},
+ "token": {"name": "token", "type": TYPE_STRING, "required": false},
+ "users": {"name": "users", "type": TYPE_ARRAY, "required": false, "content": "MatchmakerUser"},
+ "self": {"name": "self_user", "type": "MatchmakerUser", "required": true}
+ }
+
+ # The id used to join the match.
+ # A match ID used to join the match.
+ var match_id : String
+
+ # The ticket sent by the server when the user requested to matchmake for other players.
+ var ticket : String
+
+ # The token used to join a match.
+ var token : String
+
+ # The other users matched with this user and the parameters they sent.
+ var users : Array # MatchmakerUser
+
+ # The current user who matched with opponents.
+ var self_user : MatchmakerUser
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "<MatchmakerMatched match_id=%s, ticket=%s, token=%s, users=%s, self=%s>" % [
+ match_id, ticket, token, users, self_user
+ ]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchmakerMatched:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchmakerMatched", p_dict), MatchmakerMatched) as MatchmakerMatched
+
+ static func get_result_key() -> String:
+ return "matchmaker_matched"
+
+
+# The matchmaker ticket received from the server.
+class MatchmakerTicket extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "ticket": {"name": "ticket", "type": TYPE_STRING, "required": true}
+ }
+
+ # The ticket generated by the matchmaker.
+ var ticket : String
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchmakerTicket:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchmakerTicket", p_dict), MatchmakerTicket) as MatchmakerTicket
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "<MatchmakerTicket ticket=%s>" % ticket
+
+ static func get_result_key() -> String:
+ return "matchmaker_ticket"
+
+
+# The user with the parameters they sent to the server when asking for opponents.
+class MatchmakerUser extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "presence": {"name": "presence", "type": "UserPresence", "required": true},
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": false},
+ "string_properties": {"name": "string_properties", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ "numeric_properties": {"name": "numeric_properties", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_FLOAT},
+ }
+
+ # The presence of the user.
+ var presence : UserPresence
+
+ # Party identifier, if this user was matched as a party member.
+ var party_id : String
+
+ # The numeric properties which this user asked to matchmake with.
+ var numeric_properties : Dictionary
+
+ # The string properties which this user asked to matchmake with.
+ var string_properties : Dictionary
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "<MatchmakerUser presence=%s, numeric_properties=%s, string_properties=%s>" % [
+ presence, numeric_properties, string_properties]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchmakerUser:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchmakerUser", p_dict), MatchmakerUser) as MatchmakerUser
+
+ static func get_result_key() -> String:
+ return "matchmaker_user"
+
+
+# Receive status updates for users.
+class Status extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "presences": {"name": "presences", "type": TYPE_ARRAY, "required": true, "content": "UserPresence"},
+ }
+
+ # The status events for the users followed.
+ var presences := Array()
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> Status:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "Status", p_dict), Status) as Status
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "<Status presences=%s>" % [presences]
+
+ static func get_result_key() -> String:
+ return "status"
+
+
+# A status update event about other users who've come online or gone offline.
+class StatusPresenceEvent extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "joins": {"name": "joins", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
+ "leaves": {"name": "leaves", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
+ }
+
+ # Presences of users who joined the server.
+ # This join information is in response to a subscription made to be notified when a user comes online.
+ var joins : Array
+
+ # Presences of users who left the server.
+ # This leave information is in response to a subscription made to be notified when a user goes offline.
+ var leaves : Array
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "StatusPresenceEvent<joins=%s, leaves=%s>" % [joins, leaves]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> StatusPresenceEvent:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "StatusPresenceEvent", p_dict), StatusPresenceEvent) as StatusPresenceEvent
+
+ static func get_result_key() -> String:
+ return "status_presence_event"
+
+
+# A realtime socket stream on the server.
+class Stream extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "mode": {"name": "mode", "type": TYPE_INT, "required": true},
+ "subject": {"name": "subject", "type": TYPE_STRING, "required": false},
+ "subcontext": {"name": "subcontext", "type": TYPE_STRING, "required": false},
+ "label": {"name": "label", "type": TYPE_STRING, "required": false},
+ }
+
+ # The mode of the stream.
+ var mode : int
+
+ # The subject of the stream. This is usually a user id.
+ var subject : String
+
+ # The descriptor of the stream. Used with direct chat messages and contains a second user id.
+ var subcontext : String
+
+ # Identifies streams which have a context across users like a chat channel room.
+ var label : String
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "Stream<mode=%s, subject=%s, subcontext=%s, label=%s>" % [mode, subject, subcontext, label]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> Stream:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "Stream", p_dict), Stream) as Stream
+
+ static func get_result_key() -> String:
+ return "stream"
+
+
+# A batch of joins and leaves on the low level stream.
+# Streams are built on to provide abstractions for matches, chat channels, etc. In most cases you'll never need to
+# interact with the low level stream itself.
+class StreamPresenceEvent extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "stream": {"name": "stream", "type": "Stream", "required": true},
+ "joins": {"name": "joins", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
+ "leaves": {"name": "leaves", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
+ }
+
+ # Presences of users who left the stream.
+ var joins : Array
+
+ # Presences of users who joined the stream.
+ var leaves : Array
+
+ # The identifier for the stream.
+ var stream : Stream = null
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "StreamPresenceEvent<stream=%s, joins=%s, leaves=%s>" % [stream, joins, leaves]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> StreamPresenceEvent:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "StreamPresenceEvent", p_dict), StreamPresenceEvent) as StreamPresenceEvent
+
+ static func get_result_key() -> String:
+ return "stream_presence_event"
+
+
+# A state change received from a stream.
+class StreamData extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "stream": {"name": "stream", "type": "Stream", "required": true},
+ "sender": {"name": "sender", "type": "UserPresence", "required": false},
+ "data": {"name": "state", "type": TYPE_STRING, "required": false},
+ "reliable": {"name": "reliable", "type": TYPE_BOOL, "required": false},
+ }
+
+ # The user who sent the state change. May be `null`.
+ var sender : UserPresence = null
+
+ # The contents of the state change.
+ var state : String
+
+ # The identifier for the stream.
+ var stream : Stream
+
+ # True if this data was delivered reliably, false otherwise.
+ var reliable : bool
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "StreamData<sender=%s, state=%s, stream=%s>" % [sender, state, stream]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> StreamData:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "StreamData", p_dict), StreamData) as StreamData
+
+ static func get_result_key() -> String:
+ return "stream_data"
+
+
+# An object which represents a connected user in the server.
+# The server allows the same user to be connected with multiple sessions. To uniquely identify them a tuple of
+# `{ node_id, user_id, session_id }` is used which is exposed as this object.
+class UserPresence extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "persistence": {"name": "persistence", "type": TYPE_BOOL, "required": false},
+ "session_id": {"name": "session_id", "type": TYPE_STRING, "required": true},
+ "status": {"name": "status", "type": TYPE_STRING, "required": false},
+ "username": {"name": "username", "type": TYPE_STRING, "required": false},
+ "user_id": {"name": "user_id", "type": TYPE_STRING, "required": true},
+ }
+
+ # If this presence generates stored events like persistent chat messages or notifications.
+ var persistence : bool
+
+ # The session id of the user.
+ var session_id : String
+
+ # The status of the user with the presence on the server.
+ var status : String
+
+ # The username for the user.
+ var username : String
+
+ # The id of the user.
+ var user_id : String
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "UserPresence<persistence=%s, session_id=%s, status=%s, username=%s, user_id=%s>" % [
+ persistence, session_id, status, username, user_id]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> UserPresence:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "UserPresence", p_dict), UserPresence) as UserPresence
+
+ static func get_result_key() -> String:
+ return "user_presence"
+
+
+class Party extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ "open": {"name": "open", "type": TYPE_BOOL, "required": false},
+ "max_size": {"name": "max_size", "type": TYPE_INT, "required": true},
+ "self": {"name": "self_presence", "type": "UserPresence", "required": true},
+ "leader": {"name": "leader", "type": "UserPresence", "required": true},
+ "presences": {"name": "presences", "type": TYPE_ARRAY, "required": false, "content": "UserPresence"},
+ }
+
+ # Unique party identifier.
+ var party_id : String
+
+ # Open flag.
+ var open : bool = false
+
+ # Maximum number of party members.
+ var max_size : int
+
+ # The presence of the current user. i.e. Your self.
+ var self_presence : NakamaRTAPI.UserPresence
+
+ # Leader.
+ var leader : NakamaRTAPI.UserPresence
+
+ # All current party members.
+ var presences : Array # of objects NakamaUserPresence
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "Party<party_id=%s, open=%s, max_size=%d, self=%s, leader=%s, presences=%s>" % [
+ party_id, open, max_size, self_presence, leader, presences]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> Party:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "Party", p_dict), Party) as Party
+
+ static func get_result_key() -> String:
+ return "party"
+
+
+# Presence update for a particular party.
+class PartyPresenceEvent extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ "joins": {"name": "joins", "type": TYPE_ARRAY, "required": false, "content": "UserPresence"},
+ "leaves": {"name": "leaves", "type": TYPE_ARRAY, "required": false, "content": "UserPresence"},
+ }
+ # The party ID.
+ var party_id : String
+ # User presences that have just joined the party.
+ var joins : Array
+ # User presences that have just left the party.
+ var leaves : Array
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "PartyPresenceEvent<party_id=%s, joins=%s, leaves=%s>" % [party_id, joins, leaves]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> PartyPresenceEvent:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "PartyPresenceEvent", p_dict), PartyPresenceEvent) as PartyPresenceEvent
+
+ static func get_result_key() -> String:
+ return "party_presence_event"
+
+
+# Announcement of a new party leader.
+class PartyLeader extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ "presence": {"name": "presence", "type": "UserPresence", "required": true},
+ }
+ # Party ID to promote a new leader for.
+ var party_id : String
+ # The presence of an existing party member to promote as the new leader.
+ var presence : NakamaRTAPI.UserPresence
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "PartyLeader<party_id=%s, presence=%s>" % [party_id, presence]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> PartyLeader:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "PartyLeader", p_dict), PartyLeader) as PartyLeader
+
+ static func get_result_key() -> String:
+ return "party_leader"
+
+
+# Incoming notification for one or more new presences attempting to join the party.
+class PartyJoinRequest extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ "presences": {"name": "presences", "type": TYPE_ARRAY, "required": false, "content": "UserPresence"},
+ }
+ # Party ID these presences are attempting to join.
+ var party_id : String
+ # Presences attempting to join.
+ var presences : Array
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "PartyJoinRequest<party_id=%s, presences=%s>" % [party_id, presences]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> PartyJoinRequest:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "PartyJoinRequest", p_dict), PartyJoinRequest) as PartyJoinRequest
+
+ static func get_result_key() -> String:
+ return "party_join_request"
+
+
+# A response from starting a new party matchmaking process.
+class PartyMatchmakerTicket extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ "ticket": {"name": "ticket", "type": TYPE_STRING, "required": true},
+ }
+ # Party ID.
+ var party_id : String
+ # The ticket that can be used to cancel matchmaking.
+ var ticket : String
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "PartyMatchmakerTicket<party_id=%s, ticket=%s>" % [party_id, ticket]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> PartyMatchmakerTicket:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "PartyMatchmakerTicket", p_dict), PartyMatchmakerTicket) as PartyMatchmakerTicket
+
+ static func get_result_key() -> String:
+ return "party_matchmaker_ticket"
+
+
+# Incoming party data delivered from the server.
+class PartyData extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ "presence": {"name": "presence", "type": "UserPresence", "required": false},
+ "op_code": {"name": "op_code", "type": TYPE_INT, "required": true},
+ "data": {"name": "data", "type": TYPE_STRING, "required": false}
+ }
+ # The party ID.
+ var party_id : String
+ # A reference to the user presence that sent this data, if any.
+ var presence : NakamaRTAPI.UserPresence
+ # Op code value.
+ var op_code : int
+
+ # The raw base64-encoded contents of the state change.
+ var base64_data : String
+
+ # The contents of the state change decoded as a UTF-8 string.
+ var _data
+ var data : String:
+ get:
+ if _data == null and base64_data != '':
+ _data = Marshalls.base64_to_utf8(base64_data)
+ return _data if _data != null else ''
+ set(v):
+ _data = v
+
+ # The contents of the state change decoded as binary data.
+ var _binary_data
+ var binary_data : PackedByteArray:
+ get:
+ if _binary_data == null and base64_data != '':
+ _binary_data = Marshalls.base64_to_raw(base64_data)
+ return _binary_data
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "PartyData<party_id=%s, presence=%s, op_code=%d, data%s>" % [party_id, presence, op_code, data]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> PartyData:
+ var out := _safe_ret(NakamaSerializer.deserialize(p_ns, "PartyData", p_dict), PartyData) as PartyData
+ # Store the base64 data, ready to be decoded when the developer requests it.
+ if out._data != null:
+ out.base64_data = out._data
+ out._data = null
+ return out
+
+ static func get_result_key() -> String:
+ return "party_data"
+
+# End a party, kicking all party members and closing it. (this is both a message and a result)
+class PartyClose extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ }
+ # Party ID to close.
+ var party_id : String
+
+ func _init(p_ex = null):
+ super(p_ex)
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "party_close"
+
+ func _to_string():
+ if is_exception(): return get_exception()._to_string()
+ return "PartyClose<party_id=%s>" % [party_id]
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> PartyClose:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "PartyClose", p_dict), PartyClose) as PartyClose
+
+ static func get_result_key() -> String:
+ return "party_close"
diff --git a/game/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd.uid b/game/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd.uid
new file mode 100644
index 0000000..486a789
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd.uid
@@ -0,0 +1 @@
+uid://cwolb5a4yehqx
diff --git a/game/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd b/game/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd
new file mode 100644
index 0000000..d3bedd6
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd
@@ -0,0 +1,635 @@
+extends RefCounted
+class_name NakamaRTMessage
+
+# Send a channel join message to the server.
+class ChannelJoin:
+
+ const _SCHEMA = {
+ "persistence": {"name": "persistence", "type": TYPE_BOOL, "required": true},
+ "hidden": {"name": "hidden", "type": TYPE_BOOL, "required": true},
+ "target": {"name": "target", "type": TYPE_STRING, "required": true},
+ "type": {"name": "type", "type": TYPE_INT, "required": true},
+ }
+
+ enum ChannelType {
+ # A chat room which can be created dynamically with a name.
+ Room = 1,
+ # A private chat between two users.
+ DirectMessage = 2,
+ # A chat within a group on the server.
+ Group = 3
+ }
+
+ var persistence : bool
+ var hidden : bool
+ var target : String
+ var type : int
+
+ func _init(p_target : String, p_type : int, p_persistence : bool, p_hidden : bool):
+ persistence = p_persistence
+ hidden = p_hidden
+ target = p_target
+ type = p_type if p_type >= ChannelType.Room and p_type <= ChannelType.Group else 0 # Will cause error server side
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "channel_join"
+
+ func _to_string():
+ return "ChannelJoin<persistence=%s, hidden=%s, target=%s, type=%d>" % [persistence, hidden, target, type]
+
+
+# A leave message for a match on the server.
+class ChannelLeave extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "channel_id": {"name": "channel_id", "type": TYPE_STRING, "required": true}
+ }
+ var channel_id : String
+
+ func _init(p_channel_id : String):
+ channel_id = p_channel_id
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "channel_leave"
+
+ func _to_string():
+ return "ChannelLeave<channel_id=%s>" % [channel_id]
+
+
+class ChannelMessageRemove extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "channel_id": {"name": "channel_id", "type": TYPE_STRING, "required": true},
+ "message_id": {"name": "message_id", "type": TYPE_STRING, "required": true}
+ }
+
+ var channel_id : String
+ var message_id : String
+
+ func _init(p_channel_id : String, p_message_id):
+ channel_id = p_channel_id
+ message_id = p_message_id
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "channel_message_remove"
+
+ func _to_string():
+ return "ChannelMessageRemove<channel_id=%s, message_id=%s>" % [channel_id, message_id]
+
+
+# Send a chat message to a channel on the server.
+class ChannelMessageSend extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "channel_id": {"name": "channel_id", "type": TYPE_STRING, "required": true},
+ "content": {"name": "content", "type": TYPE_STRING, "required": true}
+ }
+
+ var channel_id : String
+ var content : String
+
+ func _init(p_channel_id : String, p_content):
+ channel_id = p_channel_id
+ content = p_content
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "channel_message_send"
+
+ func _to_string():
+ return "ChannelMessageSend<channel_id=%s, content=%s>" % [channel_id, content]
+
+
+class ChannelMessageUpdate extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "channel_id": {"name": "channel_id", "type": TYPE_STRING, "required": true},
+ "message_id": {"name": "message_id", "type": TYPE_STRING, "required": true},
+ "content": {"name": "content", "type": TYPE_STRING, "required": true}
+ }
+
+ var channel_id : String
+ var message_id : String
+ var content : String
+
+ func _init(p_channel_id : String, p_message_id, p_content : String):
+ channel_id = p_channel_id
+ message_id = p_message_id
+ content = p_content
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "channel_message_update"
+
+ func _to_string():
+ return "ChannelMessageUpdate<channel_id=%s, message_id=%s, content=%s>" % [channel_id, message_id, content]
+
+
+# A create message for a match on the server.
+class MatchCreate extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "name": {"name": "name", "type": TYPE_STRING, "required": false},
+ }
+
+ var name = null
+
+ func _init(p_name = null):
+ name = p_name if p_name else null
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "match_create"
+
+ func _to_string():
+ return "MatchCreate<name=%s>" % [name]
+
+
+# A join message for a match on the server.
+class MatchJoin extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "match_id": {"name": "match_id", "type": TYPE_STRING, "required": false},
+ "token": {"name": "token", "type": TYPE_STRING, "required": false},
+ "metadata": {"name": "metadata", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ }
+
+ # These two are mutually exclusive and set manually by socket for now, so use null.
+ var match_id = null
+ var token = null
+ var metadata = null
+
+ func _init(p_ex=null):
+ super(p_ex)
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "match_join"
+
+ func _to_string():
+ return "MatchJoin<match_id=%s, token=%s, metadata=%s>" % [match_id, token, metadata]
+
+
+# A leave message for a match on the server.
+class MatchLeave extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "match_id": {"name": "match_id", "type": TYPE_STRING, "required": true}
+ }
+ var match_id : String
+
+ func _init(p_match_id : String):
+ match_id = p_match_id
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "match_leave"
+
+ func _to_string():
+ return "MatchLeave<match_id=%s>" % [match_id]
+
+
+# Send new state to a match on the server.
+class MatchDataSend extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "match_id": {"name": "match_id", "type": TYPE_STRING, "required": true},
+ "op_code": {"name": "op_code", "type": TYPE_INT, "required": true},
+ "presences": {"name": "presences", "type": TYPE_ARRAY, "required": false, "content": "UserPresence"},
+ "data": {"name": "data", "type": TYPE_STRING, "required": true},
+ }
+
+ var match_id : String
+ var presences = null
+ var op_code : int
+ var data : String
+
+ func _init(p_match_id : String, p_op_code : int, p_data : String, p_presences):
+ match_id = p_match_id
+ presences = p_presences
+ op_code = p_op_code
+ data = p_data
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key():
+ return "match_data_send"
+
+ func _to_string():
+ return "MatchDataSend<match_id=%s, op_code=%s, presences=%s, data=%s>" % [match_id, op_code, presences, data]
+
+
+# Add the user to the matchmaker pool with properties.
+class MatchmakerAdd extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "query": {"name": "query", "type": TYPE_STRING, "required": true},
+ "max_count": {"name": "max_count", "type": TYPE_INT, "required": true},
+ "min_count": {"name": "min_count", "type": TYPE_INT, "required": true},
+ "numeric_properties": {"name": "numeric_properties", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_FLOAT},
+ "string_properties": {"name": "string_properties", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ "count_multiple": {"name": "count_multiple", "type": TYPE_INT, "required": false},
+ }
+
+ var query : String = "*"
+ var max_count : int = 8
+ var min_count : int = 2
+ var string_properties : Dictionary
+ var numeric_properties : Dictionary
+ var count_multiple
+
+ func _init(p_query : String = "*", p_min_count : int = 2, p_max_count : int = 8,
+ p_string_props : Dictionary = Dictionary(), p_numeric_props : Dictionary = Dictionary(),
+ p_count_multiple : int = 0):
+ query = p_query
+ min_count = p_min_count
+ max_count = p_max_count
+ string_properties = p_string_props
+ numeric_properties = p_numeric_props
+ count_multiple = p_count_multiple if p_count_multiple > 0 else null
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "matchmaker_add"
+
+ func _to_string():
+ return "MatchmakerAdd<query=%s, max_count=%d, min_count=%d, numeric_properties=%s, string_properties=%s, count_multiple=%s>" % [query, max_count, min_count, numeric_properties, string_properties, count_multiple]
+
+
+# Remove the user from the matchmaker pool by ticket.
+class MatchmakerRemove extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "ticket": {"name": "ticket", "type": TYPE_STRING, "required": true}
+ }
+
+ var ticket : String
+
+ func _init(p_ticket : String):
+ ticket = p_ticket
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "matchmaker_remove"
+
+ func _to_string():
+ return "MatchmakerRemove<ticket=%s>" % [ticket]
+
+
+# Follow one or more other users for status updates.
+class StatusFollow extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "user_ids": {"name": "user_ids", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ "usernames": {"name": "usernames", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ }
+
+ var user_ids := PackedStringArray()
+ var usernames := PackedStringArray()
+
+ func _init(p_ids : PackedStringArray, p_usernames : PackedStringArray):
+ user_ids = p_ids
+ usernames = p_usernames
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "status_follow"
+
+ func _to_string():
+ return "StatusFollow<user_ids=%s, usernames=%s>" % [user_ids, usernames]
+
+
+# Unfollow one or more users on the server.
+class StatusUnfollow extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "user_ids": {"name": "user_ids", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ }
+
+ var user_ids := PackedStringArray()
+
+ func _init(p_ids : PackedStringArray):
+ user_ids = p_ids
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "status_unfollow"
+
+ func _to_string():
+ return "StatusUnfollow<user_ids=%s>" % [user_ids]
+
+
+# Unfollow one or more users on the server.
+class StatusUpdate extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "status": {"name": "status", "type": TYPE_STRING, "required": true},
+ }
+
+ var status : String
+
+ func _init(p_status : String):
+ status = p_status
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "status_update"
+
+ func _to_string():
+ return "StatusUpdate<status=%s>" % [status]
+
+# Create a party.
+class PartyCreate extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "open": {"name": "open", "type": TYPE_BOOL, "required": true},
+ "max_size": {"name": "max_size", "type": TYPE_INT, "required": true},
+ }
+ # Whether or not the party will require join requests to be approved by the party leader.
+ var open : bool
+ # Maximum number of party members.
+ var max_size : int
+
+ func _init(p_open : bool, p_max_size : int):
+ open = p_open
+ max_size = p_max_size
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "party_create"
+
+ func _to_string():
+ return "PartyCreate<open=%s, max_size=%d>" % [open, max_size]
+
+
+# Join a party, or request to join if the party is not open.
+class PartyJoin extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ }
+ # Party ID to join.
+ var party_id : String
+
+ func _init(p_id : String):
+ party_id = p_id
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "party_join"
+
+ func _to_string():
+ return "PartyJoin<party_id=%s>" % [party_id]
+
+
+# Leave a party.
+class PartyLeave extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ }
+ # Party ID to leave.
+ var party_id : String
+
+ func _init(p_id : String):
+ party_id = p_id
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "party_leave"
+
+ func _to_string():
+ return "PartyLeave<party_id=%s>" % [party_id]
+
+
+# Promote a new party leader.
+class PartyPromote extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ "presence": {"name": "presence", "type": "UserPresence", "required": true},
+ }
+ # Party ID to promote a new leader for.
+ var party_id : String
+ # The presence of an existing party member to promote as the new leader.
+ var presence : NakamaRTAPI.UserPresence
+
+ func _init(p_id : String, p_presence : NakamaRTAPI.UserPresence):
+ party_id = p_id
+ presence = p_presence
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "party_promote"
+
+ func _to_string():
+ return "PartyPromote<party_id=%s, presence=%s>" % [party_id, presence]
+
+
+# Accept a request to join.
+class PartyAccept extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ "presence": {"name": "presence", "type": "UserPresence", "required": true},
+ }
+ # Party ID to accept a join request for.
+ var party_id : String
+ # The presence to accept as a party member.
+ var presence : NakamaRTAPI.UserPresence
+
+ func _init(p_id : String, p_presence : NakamaRTAPI.UserPresence):
+ party_id = p_id
+ presence = p_presence
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "party_accept"
+
+ func _to_string():
+ return "PartyAccept<party_id=%s, presence=%s>" % [party_id, presence]
+
+
+# Kick a party member, or decline a request to join.
+class PartyRemove extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ "presence": {"name": "presence", "type": "UserPresence", "required": true},
+ }
+ # Party ID to remove/reject from.
+ var party_id : String
+ # The presence to remove or reject.
+ var presence : NakamaRTAPI.UserPresence
+
+ func _init(p_id : String, p_presence : NakamaRTAPI.UserPresence):
+ party_id = p_id
+ presence = p_presence
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "party_remove"
+
+ func _to_string():
+ return "PartyRemove<party_id=%s, presence=%s>" % [party_id, presence]
+
+
+# Request a list of pending join requests for a party.
+class PartyJoinRequestList extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ }
+ # Party ID to get a list of join requests for.
+ var party_id : String
+
+ func _init(p_id : String):
+ party_id = p_id
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "party_join_request_list"
+
+ func _to_string():
+ return "PartyJoinRequestList<party_id=%s>" % [party_id]
+
+
+# Begin matchmaking as a party.
+class PartyMatchmakerAdd extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ "min_count": {"name": "min_count", "type": TYPE_INT, "required": true},
+ "max_count": {"name": "max_count", "type": TYPE_INT, "required": true},
+ "query": {"name": "query", "type": TYPE_STRING, "required": false},
+ "string_properties": {"name": "string_properties", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
+ "numeric_properties": {"name": "numeric_properties", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_FLOAT},
+ }
+
+ # Party ID.
+ var party_id : String
+ # Minimum total user count to match together.
+ var min_count : int
+ # Maximum total user count to match together.
+ var max_count : int
+ # Filter query used to identify suitable users.
+ var query : String
+ # String properties.
+ var string_properties : Dictionary
+ # Numeric properties.
+ var numeric_properties : Dictionary
+ # Optional multiple of the count that must be satisfied.
+ var count_multiple
+
+ func _init(p_id : String, p_min_count : int, p_max_count : int, p_query : String, p_string_properties = null, p_numeric_properties = null, p_count_multiple = null):
+ party_id = p_id
+ min_count = p_min_count
+ max_count = p_max_count
+ query = p_query
+ string_properties = p_string_properties
+ numeric_properties = p_numeric_properties
+ count_multiple = p_count_multiple
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "party_matchmaker_add"
+
+ func _to_string():
+ return "PartyMatchmakerAdd<party_id=%s, min_count=%d, max_count=%d, query=%s, string_properties=%s, numeric_properties=%s, count_multiple=%s>" % [party_id, min_count, max_count, query, string_properties, numeric_properties, count_multiple]
+
+
+# Cancel a party matchmaking process using a ticket.
+class PartyMatchmakerRemove extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ "ticket": {"name": "ticket", "type": TYPE_STRING, "required": true},
+ }
+ # Party ID.
+ var party_id : String
+ # The ticket to cancel.
+ var ticket : String
+
+ func _init(p_id : String, p_ticket : String):
+ party_id = p_id
+ ticket = p_ticket
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "party_matchmaker_remove"
+
+ func _to_string():
+ return "PartyMatchmakerRemove<party_id=%s, ticket=%s>" % [party_id, ticket]
+
+
+# Send data to a party.
+class PartyDataSend extends NakamaAsyncResult:
+ const _SCHEMA = {
+ "party_id": {"name": "party_id", "type": TYPE_STRING, "required": true},
+ "op_code": {"name": "op_code", "type": TYPE_INT, "required": true},
+ "data": {"name": "data", "type": TYPE_STRING, "required": false}
+ }
+ # Party ID to send to.
+ var party_id : String
+ # Op code value.
+ var op_code : int
+ # Data payload, if any.
+ var data = null
+
+ func _init(p_id : String, p_op_code : int, p_data = null):
+ party_id = p_id
+ op_code = p_op_code
+ data = p_data
+
+ func serialize():
+ return NakamaSerializer.serialize(self)
+
+ func get_msg_key() -> String:
+ return "party_data_send"
+
+ func _to_string():
+ return "PartyDataSend<party_id=%s, op_code=%d, data=%s>" % [party_id, op_code, data]
diff --git a/game/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd.uid b/game/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd.uid
new file mode 100644
index 0000000..1d8c859
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd.uid
@@ -0,0 +1 @@
+uid://dmnuihb6comgt
diff --git a/game/addons/com.heroiclabs.nakama/api/NakamaSession.gd b/game/addons/com.heroiclabs.nakama/api/NakamaSession.gd
new file mode 100644
index 0000000..f8492ad
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/api/NakamaSession.gd
@@ -0,0 +1,157 @@
+extends NakamaAsyncResult
+class_name NakamaSession
+
+var _created : bool = false
+var created : bool:
+ set(v):
+ pass
+ get:
+ return _created
+
+var _token : String = ""
+var token : String:
+ set(v):
+ pass
+ get:
+ return _token
+
+var _create_time : int = 0
+var create_time : int:
+ set(v):
+ pass
+ get:
+ return _create_time
+
+var _expire_time : int = 0
+var expire_time : int:
+ set(v):
+ pass
+ get:
+ return _expire_time
+
+var expired : bool:
+ set(v):
+ pass
+ get:
+ return is_expired()
+
+var _vars : Dictionary = {}
+var vars : Dictionary:
+ set(v):
+ pass
+ get:
+ return _vars
+
+var _username : String = ""
+var username : String:
+ set(v):
+ pass
+ get:
+ return _username
+
+var _user_id : String = ""
+var user_id : String:
+ set(v):
+ pass
+ get:
+ return _user_id
+
+var _refresh_token : String = ""
+var refresh_token : String:
+ set(v):
+ pass
+ get:
+ return _refresh_token
+
+var _refresh_expire_time : int = 0
+var refresh_expire_time : int:
+ set(v):
+ pass
+ get:
+ return _refresh_expire_time
+
+var _valid : bool = false
+var valid : bool:
+ set(v):
+ pass
+ get:
+ return _valid
+
+func is_expired() -> bool:
+ return _expire_time < Time.get_unix_time_from_system()
+
+func would_expire_in(p_secs : int) -> bool:
+ return _expire_time < Time.get_unix_time_from_system() + p_secs
+
+func is_refresh_expired() -> bool:
+ return _refresh_expire_time < Time.get_unix_time_from_system()
+
+func is_valid():
+ return _valid
+
+func _init(p_token = null, p_created : bool = false, p_refresh_token = null, p_exception = null):
+ super(p_exception)
+
+ if p_token:
+ _created = p_created
+ _parse_token(p_token)
+ if p_refresh_token:
+ _parse_refresh_token(p_refresh_token)
+
+func refresh(p_session):
+ if p_session.token:
+ _parse_token(p_session.token)
+ if p_session.refresh_token:
+ _parse_refresh_token(p_session.refresh_token)
+
+func _parse_token(p_token):
+ var decoded = _jwt_unpack(p_token)
+ if decoded.is_empty():
+ _valid = false
+ return
+ _valid = true
+ _token = p_token
+ _create_time = Time.get_unix_time_from_system()
+ _expire_time = int(decoded.get("exp", 0))
+ _username = str(decoded.get("usn", ""))
+ _user_id = str(decoded.get("uid", ""))
+ _vars = {}
+ if decoded.has("vrs") and typeof(decoded["vrs"]) == TYPE_DICTIONARY:
+ for k in decoded["vrs"]:
+ _vars[k] = decoded["vrs"][k]
+
+func _parse_refresh_token(p_refresh_token):
+ var decoded = _jwt_unpack(p_refresh_token)
+ if decoded.is_empty():
+ return
+ _refresh_expire_time = int(decoded.get("exp", 0))
+ _refresh_token = p_refresh_token
+
+func _to_string():
+ if is_exception():
+ return get_exception()._to_string()
+ return "Session<created=%s, token=%s, create_time=%d, username=%s, user_id=%s, vars=%s, expire_time=%d, refresh_token=%s refresh_expire_time=%d>" % [
+ _created, _token, _create_time, _username, _user_id, str(_vars), _expire_time, _refresh_token, _refresh_expire_time]
+
+func _jwt_unpack(p_token : String) -> Dictionary:
+ # Hack decode JSON payload from JWT.
+ if p_token.find(".") == -1:
+ _ex = NakamaException.new("Missing payload: %s" % p_token)
+ return {}
+ var payload = p_token.split('.')[1];
+ var pad_length = ceil(payload.length() / 4.0) * 4;
+ # Pad base64
+ for i in range(0, pad_length - payload.length()):
+ payload += "="
+ payload = payload.replace("-", "+").replace("_", "/")
+ var unpacked = Marshalls.base64_to_utf8(payload)
+
+ var json = JSON.new()
+ var error = json.parse(unpacked)
+
+ if error == OK:
+ var decoded = json.get_data()
+ if typeof(decoded) == TYPE_DICTIONARY:
+ return decoded
+ _ex = NakamaException.new("Unable to unpack token: %s" % p_token)
+ return {}
diff --git a/game/addons/com.heroiclabs.nakama/api/NakamaSession.gd.uid b/game/addons/com.heroiclabs.nakama/api/NakamaSession.gd.uid
new file mode 100644
index 0000000..4e17b99
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/api/NakamaSession.gd.uid
@@ -0,0 +1 @@
+uid://crt2fnhvrxl7q
diff --git a/game/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd b/game/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd
new file mode 100644
index 0000000..d157af6
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd
@@ -0,0 +1,34 @@
+extends RefCounted
+class_name NakamaStorageObjectId
+
+# The collection which stores the object.
+var collection : String
+
+# The key of the object within the collection.
+var key : String
+
+# The user owner of the object.
+var user_id : String
+
+# The version hash of the object.
+var version : String
+
+func _init(p_collection, p_key, p_user_id = "", p_version = ""):
+ collection = p_collection
+ key = p_key
+ user_id = p_user_id
+ version = p_version
+
+func as_delete():
+ return NakamaAPI.ApiDeleteStorageObjectId.create(NakamaAPI, {
+ "collection": collection,
+ "key": key,
+ "version": version
+ })
+
+func as_read():
+ return NakamaAPI.ApiReadStorageObjectId.create(NakamaAPI, {
+ "collection": collection,
+ "key": key,
+ "user_id": user_id
+ })
diff --git a/game/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd.uid b/game/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd.uid
new file mode 100644
index 0000000..cdde889
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd.uid
@@ -0,0 +1 @@
+uid://cef834r37xw6r
diff --git a/game/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd b/game/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd
new file mode 100644
index 0000000..c1fdb6c
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd
@@ -0,0 +1,28 @@
+extends RefCounted
+class_name NakamaWriteStorageObject
+
+var collection : String
+var key : String
+var permission_read : int = 0
+var permission_write : int = 0
+var value : String
+var version : String
+
+func _init(p_collection : String, p_key : String, p_permission_read : int,
+ p_permission_write : int, p_value : String, p_version : String):
+ collection = p_collection
+ key = p_key
+ permission_read = p_permission_read
+ permission_write = p_permission_write
+ value = p_value
+ version = p_version
+
+func as_write():
+ return NakamaAPI.ApiWriteStorageObject.create(NakamaAPI, {
+ "collection": collection,
+ "key": key,
+ "permission_read": permission_read,
+ "permission_write": permission_write,
+ "value": value,
+ "version": version
+ })
diff --git a/game/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd.uid b/game/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd.uid
new file mode 100644
index 0000000..c93d23a
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd.uid
@@ -0,0 +1 @@
+uid://dk52mhovdsa8n
diff --git a/game/addons/com.heroiclabs.nakama/client/NakamaClient.gd b/game/addons/com.heroiclabs.nakama/client/NakamaClient.gd
new file mode 100644
index 0000000..84ff608
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/client/NakamaClient.gd
@@ -0,0 +1,1084 @@
+extends RefCounted
+
+# A client for the API in Nakama server.
+class_name NakamaClient
+
+const ChannelType = NakamaRTMessage.ChannelJoin.ChannelType
+
+# The host address of the server. Defaults to "127.0.0.1".
+var _host
+var host : String:
+ set(v):
+ pass
+ get:
+ return _host
+
+# The port number of the server. Defaults to 7350.
+var _port
+var port : int:
+ set(v):
+ pass
+ get:
+ return _port
+
+# The protocol scheme used to connect with the server. Must be either "http" or "https".
+var _scheme
+var scheme : String:
+ set(v):
+ pass
+ get:
+ return _scheme
+
+# The key used to authenticate with the server without a session. Defaults to "defaultkey".
+var _server_key : String = "defaultkey"
+var server_key:
+ set(v):
+ pass
+ get:
+ return _server_key
+
+# Set the timeout in seconds on requests sent to the server.
+var timeout : int
+
+var logger : NakamaLogger = null
+
+var _api_client : NakamaAPI.ApiClient
+
+var auto_refresh : bool = true:
+ set(v):
+ set_auto_refresh(v)
+ get:
+ return get_auto_refresh()
+
+var auto_refresh_seconds : int = true:
+ set(v):
+ set_auto_refresh_seconds(v)
+ get:
+ return get_auto_refresh_seconds()
+
+var auto_retry : bool = true:
+ set(v):
+ set_auto_retry(v)
+ get:
+ return get_auto_retry()
+
+var auto_retry_count:
+ set(v):
+ set_auto_retry_count(v)
+ get:
+ return get_auto_retry_count()
+
+var auto_retry_backoff_base:
+ set(v):
+ set_auto_retry_backoff_base(v)
+ get:
+ return get_auto_retry_backoff_base()
+
+var last_cancel_token:
+ set(v):
+ pass
+ get:
+ return get_last_cancel_token()
+
+func get_auto_refresh():
+ return _api_client.auto_refresh
+
+func set_auto_refresh(p_value):
+ _api_client.auto_refresh = p_value
+
+func get_auto_refresh_seconds():
+ return _api_client.auto_refresh_time
+
+func set_auto_refresh_seconds(p_value):
+ _api_client.auto_refresh_time = p_value
+
+func get_last_cancel_token():
+ return _api_client.last_cancel_token
+
+func get_auto_retry():
+ return _api_client.auto_retry
+
+func set_auto_retry(p_value):
+ _api_client.auto_retry = p_value
+
+func get_auto_retry_count():
+ return _api_client.auto_retry_count
+
+func set_auto_retry_count(p_value):
+ _api_client.auto_retry_count = p_value
+
+func get_auto_retry_backoff_base():
+ return _api_client.auto_retry_backoff_base
+
+func set_auto_retry_backoff_base(p_value):
+ _api_client.auto_retry_backoff_base = p_value
+
+func cancel_request(p_token):
+ _api_client.cancel_request(p_token)
+
+func _init(p_adapter : NakamaHTTPAdapter,
+ p_server_key : String,
+ p_scheme : String,
+ p_host : String,
+ p_port : int,
+ p_timeout : int):
+
+ _server_key = p_server_key
+ _scheme = p_scheme
+ _host = p_host
+ _port = p_port
+ timeout = p_timeout
+ logger = p_adapter.logger
+ _api_client = NakamaAPI.ApiClient.new(_scheme + "://" + _host + ":" + str(_port), p_adapter, NakamaAPI, _server_key, p_timeout)
+
+# Restore a session from the auth token.
+# A `null` or empty authentication token will return `null`.
+# @param authToken - The authentication token to restore as a session.
+# Returns a session.
+static func restore_session(auth_token : String):
+ return NakamaSession.new(auth_token, false)
+
+func _to_string():
+ return "Client(Host='%s', Port=%s, Scheme='%s', ServerKey='%s', Timeout=%s)" % [
+ host, port, scheme, server_key, timeout
+ ]
+
+func _parse_auth(p_session) -> NakamaSession:
+ if p_session.is_exception():
+ return NakamaSession.new(null, false, null, p_session.get_exception())
+ return NakamaSession.new(p_session.token, p_session.created, p_session.refresh_token)
+
+# Add one or more friends by id or username.
+# @param p_session - The session of the user.
+# @param p_ids - The ids of the users to add or invite as friends.
+# @param p_usernames - The usernames of the users to add as friends.
+# Returns a task which represents the asynchronous operation.
+func add_friends_async(p_session : NakamaSession, p_ids = null, p_usernames = null) -> NakamaAsyncResult:
+ return await _api_client.add_friends_async(p_session, p_ids, p_usernames)
+
+# Add one or more users to the group.
+# @param p_session - The session of the user.
+# @param p_group_id - The id of the group to add users into.
+# @param p_ids - The ids of the users to add or invite to the group.
+# Returns a task which represents the asynchronous operation.
+func add_group_users_async(p_session : NakamaSession, p_group_id : String, p_ids : PackedStringArray) -> NakamaAsyncResult:
+ return await _api_client.add_group_users_async(p_session, p_group_id, p_ids);
+
+# Authenticate a user with an Apple ID against the server.
+# @param p_username - A username used to create the user.</param>
+# @param p_token - The ID token received from Apple to validate.</param>
+# @param p_vars - Extra information that will be bundled in the session token.</param>
+# Returns a task which resolves to a session object.
+func authenticate_apple_async(p_token : String, p_username = null, p_create : bool = true, p_vars = null) -> NakamaSession:
+ return _parse_auth(await _api_client.authenticate_apple_async(server_key, "",
+ NakamaAPI.ApiAccountApple.create(NakamaAPI, {
+ "token": p_token,
+ "vars": p_vars
+ }), p_create, p_username))
+
+# Authenticate a user with a custom id.
+# @param p_id - A custom identifier usually obtained from an external authentication service.
+# @param p_username - A username used to create the user. May be `null`.
+# @param p_create - If the user should be created when authenticated.
+# @param p_vars - Extra information that will be bundled in the session token.
+# Returns a task which resolves to a session object.
+func authenticate_custom_async(p_id : String, p_username = null, p_create : bool = true, p_vars = null) -> NakamaSession:
+ return _parse_auth(await _api_client.authenticate_custom_async(server_key, "",
+ NakamaAPI.ApiAccountCustom.create(NakamaAPI, {
+ "id": p_id,
+ "vars": p_vars
+ }), p_create, p_username))
+
+# Authenticate a user with a device id.
+# @param p_id - A device identifier usually obtained from a platform API.
+# @param p_username - A username used to create the user. May be `null`.
+# @param p_create - If the user should be created when authenticated.
+# @param p_vars - Extra information that will be bundled in the session token.
+# Returns a task which resolves to a session object.
+func authenticate_device_async(p_id : String, p_username = null, p_create : bool = true, p_vars = null) -> NakamaSession:
+ return _parse_auth(await _api_client.authenticate_device_async(server_key, "",
+ NakamaAPI.ApiAccountDevice.create(NakamaAPI, {
+ "id": p_id,
+ "vars": p_vars
+ }), p_create, p_username))
+
+# Authenticate a user with an email and password.
+# @param p_email - The email address of the user.
+# @param p_password - The password for the user.
+# @param p_username - A username used to create the user. May be `null`.
+# @param p_create - If the user should be created when authenticated.
+# @param p_vars - Extra information that will be bundled in the session token.
+# Returns a task which resolves to a session object.
+func authenticate_email_async(p_email : String, p_password : String, p_username = null, p_create : bool = true, p_vars = null) -> NakamaSession:
+ return _parse_auth(await _api_client.authenticate_email_async(server_key, "",
+ NakamaAPI.ApiAccountEmail.create(NakamaAPI, {
+ "email": p_email,
+ "password": p_password,
+ "vars": p_vars
+ }), p_create, p_username))
+
+# Authenticate a user with a Facebook auth token.
+# @param p_token - An OAuth access token from the Facebook SDK.
+# @param p_username - A username used to create the user. May be `null`.
+# @param p_create - If the user should be created when authenticated.
+# @param p_import - If the Facebook friends should be imported.
+# @param p_vars - Extra information that will be bundled in the session token.
+# Returns a task which resolves to a session object.
+func authenticate_facebook_async(p_token : String, p_username = null, p_create : bool = true, p_import : bool = true, p_vars = null) -> NakamaSession:
+ return _parse_auth(await _api_client.authenticate_facebook_async(server_key, "",
+ NakamaAPI.ApiAccountFacebook.create(NakamaAPI, {
+ "token": p_token,
+ "vars": p_vars
+ }), p_create, p_username, p_import))
+
+# Authenticate a user with a Facebook Instant Game token against the server.
+# @param p_signed_player_info - Facebook Instant Game signed info from Facebook SDK.
+# @param p_username - A username used to create the user. May be `null`.
+# @param p_create - If the user should be created when authenticated.
+# @param p_import - If the Facebook friends should be imported.
+# @param p_vars - Extra information that will be bundled in the session token.
+# Returns a task which resolves to a session object.
+func authenticate_facebook_instant_game_async(p_signed_player_info : String, p_username = null, p_create : bool = true, p_vars = null) -> NakamaSession:
+ return _parse_auth(await _api_client.authenticate_facebook_instant_game_async(server_key, "",
+ NakamaAPI.ApiAccountFacebookInstantGame.create(NakamaAPI, {
+ "signed_player_info": p_signed_player_info,
+ "vars": p_vars
+ }), p_create, p_username))
+
+# Authenticate a user with Apple Game Center.
+# @param p_bundle_id - The bundle id of the Game Center application.
+# @param p_player_id - The player id of the user in Game Center.
+# @param p_public_key_url - The URL for the public encryption key.
+# @param p_salt - A random `NSString` used to compute the hash and keep it randomized.
+# @param p_signature - The verification signature data generated.
+# @param p_timestamp_seconds - The date and time that the signature was created.
+# @param p_username - A username used to create the user. May be `null`.
+# @param p_create - If the user should be created when authenticated.
+# @param p_vars - Extra information that will be bundled in the session token.
+# Returns a task which resolves to a session object.
+func authenticate_game_center_async(p_bundle_id : String, p_player_id : String, p_public_key_url : String,
+ p_salt : String, p_signature : String, p_timestamp_seconds : String, p_username = null, p_create : bool = true, p_vars = null) -> NakamaSession:
+ return _parse_auth(await _api_client.authenticate_game_center_async(server_key, "",
+ NakamaAPI.ApiAccountGameCenter.create(NakamaAPI, {
+ "bundle_id": p_bundle_id,
+ "player_id": p_player_id,
+ "public_key_url": p_public_key_url,
+ "salt": p_salt,
+ "signature": p_signature,
+ "timestamp_seconds": p_timestamp_seconds,
+ "vars": p_vars
+ }), p_create, p_username))
+
+# Authenticate a user with a Google auth token.
+# @param p_token - An OAuth access token from the Google SDK.
+# @param p_username - A username used to create the user. May be `null`.
+# @param p_create - If the user should be created when authenticated.
+# @param p_vars - Extra information that will be bundled in the session token.
+# Returns a task which resolves to a session object.
+func authenticate_google_async(p_token : String, p_username = null, p_create : bool = true, p_vars = null) -> NakamaSession:
+ return _parse_auth(await _api_client.authenticate_google_async(server_key, "",
+ NakamaAPI.ApiAccountGoogle.create(NakamaAPI, {
+ "token": p_token,
+ "vars": p_vars
+ }), p_create, p_username))
+
+# Authenticate a user with a Steam auth token.
+# @param p_token - An authentication token from the Steam network.
+# @param p_username - A username used to create the user. May be `null`.
+# @param p_create - If the user should be created when authenticated.
+# @param p_vars - Extra information that will be bundled in the session token.
+# Returns a task which resolves to a session object.
+func authenticate_steam_async(p_token : String, p_username = null, p_create : bool = true, p_vars = null, p_sync : bool = false) -> NakamaSession:
+ return _parse_auth(await _api_client.authenticate_steam_async(server_key, "",
+ NakamaAPI.ApiAccountSteam.create(NakamaAPI, {
+ "token": p_token,
+ "vars": p_vars
+ }), p_create, p_username, p_sync))
+
+# Block one or more friends by id or username.
+# @param p_session - The session of the user.
+# @param p_ids - The ids of the users to block.
+# @param p_usernames - The usernames of the users to block.
+# Returns a task which represents the asynchronous operation.
+func block_friends_async(p_session : NakamaSession, p_ids : PackedStringArray, p_usernames = null) -> NakamaAsyncResult:
+ return await _api_client.block_friends_async(p_session, p_ids, p_usernames);
+
+# Create a group.
+# @param p_session - The session of the user.
+# @param p_name - The name for the group.
+# @param p_description - A description for the group.
+# @param p_avatar_url - An avatar url for the group.
+# @param p_lang_tag - A language tag in BCP-47 format for the group.
+# @param p_open - If the group should have open membership.
+# @param p_max_count - The maximum number of members allowed.
+# Returns a task which resolves to a new group object.
+func create_group_async(p_session : NakamaSession, p_name : String, p_description : String = "",
+ p_avatar_url = null, p_lang_tag = null, p_open : bool = true, p_max_count : int = 100): # -> NakamaAPI.ApiGroup:
+ return await _api_client.create_group_async(p_session,
+ NakamaAPI.ApiCreateGroupRequest.create(NakamaAPI, {
+ "avatar_url": p_avatar_url,
+ "description": p_description,
+ "lang_tag": p_lang_tag,
+ "max_count": p_max_count,
+ "name": p_name,
+ "open": p_open
+ }))
+
+# Delete the current user's account on the server.
+# @param p_session - The session of the user.
+# Returns a task which represents the asynchronous operation.
+func delete_account_async(p_session : NakamaSession) -> NakamaAsyncResult:
+ return await _api_client.delete_account_async(p_session)
+
+# Delete one more or users by id or username from friends.
+# @param p_session - The session of the user.
+# @param p_ids - The user ids to remove as friends.
+# @param p_usernames - The usernames to remove as friends.
+# Returns a task which represents the asynchronous operation.
+func delete_friends_async(p_session : NakamaSession, p_ids : PackedStringArray, p_usernames = null) -> NakamaAsyncResult:
+ return await _api_client.delete_friends_async(p_session, p_ids, p_usernames)
+
+# Delete a group by id.
+# @param p_session - The session of the user.
+# @param p_group_id - The group id to to remove.
+# Returns a task which represents the asynchronous operation.
+func delete_group_async(p_session : NakamaSession, p_group_id : String) -> NakamaAsyncResult:
+ return await _api_client.delete_group_async(p_session, p_group_id)
+
+# Delete a leaderboard record.
+# @param p_session - The session of the user.
+# @param p_leaderboard_id - The id of the leaderboard with the record to be deleted.
+# Returns a task which represents the asynchronous operation.
+func delete_leaderboard_record_async(p_session : NakamaSession, p_leaderboard_id : String) -> NakamaAsyncResult:
+ return await _api_client.delete_leaderboard_record_async(p_session, p_leaderboard_id)
+
+# Delete one or more notifications by id.
+# @param p_session - The session of the user.
+# @param p_ids - The notification ids to remove.
+# Returns a task which represents the asynchronous operation.
+func delete_notifications_async(p_session : NakamaSession, p_ids : PackedStringArray) -> NakamaAsyncResult:
+ return await _api_client.delete_notifications_async(p_session, p_ids)
+
+# Delete one or more storage objects.
+# @param p_session - The session of the user.
+# @param p_ids - The ids of the objects to delete.
+# Returns a task which represents the asynchronous operation.
+func delete_storage_objects_async(p_session : NakamaSession, p_ids : Array) -> NakamaAsyncResult:
+ var ids : Array = []
+ for id in p_ids:
+ if not id is NakamaStorageObjectId:
+ continue # TODO Exceptions
+ var obj_id : NakamaStorageObjectId = id
+ ids.append(obj_id.as_delete().serialize())
+ return await _api_client.delete_storage_objects_async(p_session,
+ NakamaAPI.ApiDeleteStorageObjectsRequest.create(NakamaAPI, {
+ "object_ids": ids
+ }))
+
+# Demote a set of users in a group to the next role down.
+# @param p_session - The session of the user.
+# @param p_group_id - The ID of the group to demote users into.
+# @param p_ids - The IDs of the users to demote.
+# Returns a task which represents the asynchronous operation.
+func demote_group_users_async(p_session : NakamaSession, p_group_id : String, p_user_ids : Array):
+ return await _api_client.demote_group_users_async(p_session, p_group_id, p_user_ids)
+
+# Submit an event for processing in the server's registered runtime custom events handler.
+# @param p_session - The session of the user.
+# @param p_name - The name of the event.
+# @param p_properties - The properties of the event.
+# Returns a task which represents the asynchronous operation.
+func event_async(p_session : NakamaSession, p_name : String, p_properties : Dictionary = {}) -> NakamaAsyncResult:
+ return await _api_client.event_async(p_session, NakamaAPI.ApiEvent.create(
+ NakamaAPI,
+ {
+ "name": p_name,
+ "properties": p_properties,
+ "external": true,
+ }
+ ))
+
+# Fetch the user account owned by the session.
+# @param p_session - The session of the user.
+# Returns a task which resolves to the account object.
+func get_account_async(p_session : NakamaSession): # -> NakamaAPI.ApiAccount:
+ return await _api_client.get_account_async(p_session)
+
+# Get subscription by product id.
+# @param p_session - The session of the user.
+# @param p_product_id - The product id.
+# Returns a task which resolves to the subscription object.
+func get_subscription_async(p_session : NakamaSession, p_product_id : String): # -> ApiValidatedSubscription:
+ return await _api_client.get_subscription_async(p_session, p_product_id)
+
+# Fetch one or more users by id, usernames, and Facebook ids.
+# @param p_session - The session of the user.
+# @param p_ids - The IDs of the users to retrieve.
+# @param p_usernames - The usernames of the users to retrieve.
+# @param p_facebook_ids - The facebook IDs of the users to retrieve.
+# Returns a task which resolves to a collection of user objects.
+func get_users_async(p_session : NakamaSession, p_ids : PackedStringArray, p_usernames = null, p_facebook_ids = null): # -> NakamaAPI.ApiUsers:
+ return await _api_client.get_users_async(p_session, p_ids, p_usernames, p_facebook_ids)
+
+# Import Facebook friends and add them to the user's account.
+# The server will import friends when the user authenticates with Facebook. This function can be used to be
+# explicit with the import operation.
+# @param p_session - The session of the user.
+# @param p_token - An OAuth access token from the Facebook SDK.
+# @param p_reset - If the Facebook friend import for the user should be reset.
+# Returns a task which represents the asynchronous operation.
+func import_facebook_friends_async(p_session : NakamaSession, p_token : String, p_reset = null) -> NakamaAsyncResult:
+ return await _api_client.import_facebook_friends_async(p_session,
+ NakamaAPI.ApiAccountFacebook.create(NakamaAPI, {
+ "token": p_token
+ }), p_reset)
+
+# Import Steam friends and add them to the user's account.
+# The server will import friends when the user authenticates with Steam. This function can be used to be
+# explicit with the import operation.
+# @param p_session - The session of the user.
+# @param p_token - An access token from Steam.
+# @param p_reset - If the Steam friend import for the user should be reset.
+# Returns a task which represents the asynchronous operation.
+func import_steam_friends_async(p_session : NakamaSession, p_token : String, p_reset = null):
+ return await _api_client.import_steam_friends_async(p_session,
+ NakamaAPI.ApiAccountSteam.create(NakamaAPI, {
+ "token": p_token
+ }), p_reset)
+
+# Join a group if it has open membership or request to join it.
+# @param p_session - The session of the user.
+# @param p_group_id - The ID of the group to join.
+# Returns a task which represents the asynchronous operation.
+func join_group_async(p_session : NakamaSession, p_group_id : String) -> NakamaAsyncResult:
+ return await _api_client.join_group_async(p_session, p_group_id)
+
+# Join a tournament by ID.
+# @param p_session - The session of the user.
+# @param p_tournament_id - The ID of the tournament to join.
+# Returns a task which represents the asynchronous operation.
+func join_tournament_async(p_session : NakamaSession, p_tournament_id : String) -> NakamaAsyncResult:
+ return await _api_client.join_tournament_async(p_session, p_tournament_id)
+
+# Kick one or more users from the group.
+# @param p_session - The session of the user.
+# @param p_group_id - The ID of the group.
+# @param p_ids - The IDs of the users to kick.
+# Returns a task which represents the asynchronous operation.
+func kick_group_users_async(p_session : NakamaSession, p_group_id : String, p_ids : PackedStringArray) -> NakamaAsyncResult:
+ return await _api_client.kick_group_users_async(p_session, p_group_id, p_ids)
+
+# Leave a group by ID.
+# @param p_session - The session of the user.
+# @param p_group_id - The ID of the group to leave.
+# Returns a task which represents the asynchronous operation.
+func leave_group_async(p_session : NakamaSession, p_group_id : String) -> NakamaAsyncResult:
+ return await _api_client.leave_group_async(p_session, p_group_id)
+
+# Link an Apple ID to the social profiles on the current user's account.
+# @param p_session - The session of the user.
+# @param p_token - The ID token received from Apple to validate.
+# Returns a task which represents the asynchronous operation.
+func link_apple_async(p_session : NakamaSession, p_token : String) -> NakamaAsyncResult:
+ return await _api_client.link_apple_async(p_session, NakamaAPI.ApiAccountApple.create(NakamaAPI, {
+ "token": p_token
+ }))
+
+# Link a custom ID to the user account owned by the session.
+# @param p_session - The session of the user.
+# @param p_id - A custom identifier usually obtained from an external authentication service.
+# Returns a task which represents the asynchronous operation.
+func link_custom_async(p_session : NakamaSession, p_id : String) -> NakamaAsyncResult:
+ return await _api_client.link_custom_async(p_session, NakamaAPI.ApiAccountCustom.create(NakamaAPI, {
+ "id": p_id
+ }))
+
+# Link a device ID to the user account owned by the session.
+# @param p_session - The session of the user.
+# @param p_id - A device identifier usually obtained from a platform API.
+# Returns a task which represents the asynchronous operation.
+func link_device_async(p_session : NakamaSession, p_id : String) -> NakamaAsyncResult:
+ return await _api_client.link_device_async(p_session, NakamaAPI.ApiAccountDevice.create(NakamaAPI, {
+ "id": p_id
+ }))
+
+# Link an email with password to the user account owned by the session.
+# @param p_session - The session of the user.
+# @param p_email - The email address of the user.
+# @param p_password - The password for the user.
+# Returns a task which represents the asynchronous operation.
+func link_email_async(p_session : NakamaSession, p_email : String, p_password : String) -> NakamaAsyncResult:
+ return await _api_client.link_email_async(p_session, NakamaAPI.ApiAccountEmail.create(NakamaAPI, {
+ "email": p_email,
+ "password": p_password
+ }))
+
+# Link a Facebook profile to a user account.
+# @param p_session - The session of the user.
+# @param p_token - An OAuth access token from the Facebook SDK.
+# @param p_import - If the Facebook friends should be imported.
+# Returns a task which represents the asynchronous operation.
+func link_facebook_async(p_session : NakamaSession, p_token : String) -> NakamaAsyncResult:
+ return await _api_client.link_facebook_async(p_session, NakamaAPI.ApiAccountFacebook.create(NakamaAPI, {
+ "token": p_token
+ }))
+
+# Add Facebook Instant Game to the social profiles on the current user's account.
+# @param p_session - The session of the user.
+# @param p_token - An OAuth access token from the Facebook SDK.
+# @param p_import - If the Facebook friends should be imported.
+# Returns a task which represents the asynchronous operation.
+func link_facebook_instant_game_async(p_session : NakamaSession, p_signed_player_info : String) -> NakamaAsyncResult:
+ return await _api_client.link_facebook_instant_game_async(
+ p_session,
+ NakamaAPI.ApiAccountFacebookInstantGame.create(
+ NakamaAPI, {
+ "signed_player_info": p_signed_player_info
+ })
+ )
+
+# Link a Game Center profile to a user account.
+# @param p_session - The session of the user.
+# @param p_bundle_id - The bundle ID of the Game Center application.
+# @param p_player_id - The player ID of the user in Game Center.
+# @param p_public_key_url - The URL for the public encryption key.
+# @param p_salt - A random `NSString` used to compute the hash and keep it randomized.
+# @param p_signature - The verification signature data generated.
+# @param p_timestamp_seconds - The date and time that the signature was created.
+# Returns a task which represents the asynchronous operation.
+func link_game_center_async(p_session : NakamaSession,
+ p_bundle_id : String, p_player_id : String, p_public_key_url : String, p_salt : String, p_signature : String, p_timestamp_seconds) -> NakamaAsyncResult:
+ return await _api_client.link_game_center_async(p_session,
+ NakamaAPI.ApiAccountGameCenter.create(NakamaAPI, {
+ "bundle_id": p_bundle_id,
+ "player_id": p_player_id,
+ "public_key_url": p_public_key_url,
+ "salt": p_salt,
+ "signature": p_signature,
+ "timestamp_seconds": p_timestamp_seconds,
+ }))
+
+# Link a Google profile to a user account.
+# @param p_session - The session of the user.
+# @param p_token - An OAuth access token from the Google SDK.
+# Returns a task which represents the asynchronous operation.
+func link_google_async(p_session : NakamaSession, p_token : String) -> NakamaAsyncResult:
+ return await _api_client.link_google_async(p_session, NakamaAPI.ApiAccountGoogle.create(NakamaAPI, {
+ "token": p_token
+ }))
+
+# Link a Steam profile to a user account.
+# @param p_session - The session of the user.
+# @param p_token - An authentication token from the Steam network.
+# Returns a task which represents the asynchronous operation.
+func link_steam_async(p_session : NakamaSession, p_token : String, p_sync : bool = false) -> NakamaAsyncResult:
+ return await _api_client.link_steam_async(p_session, NakamaAPI.ApiLinkSteamRequest.create(
+ NakamaAPI,
+ {
+ "account": NakamaAPI.ApiAccountSteam.create(NakamaAPI, {
+ "token": p_token
+ }).serialize(),
+ "sync": p_sync
+ }
+
+ ))
+
+# List messages from a chat channel.
+# @param p_session - The session of the user.
+# @param p_channel_id - The id of the chat channel.
+# @param p_limit - The number of chat messages to list.
+# @param forward - Fetch messages forward from the current cursor (or the start).
+# @param p_cursor - A cursor for the current position in the messages history to list.
+# Returns a task which resolves to the channel message list object.
+func list_channel_messages_async(p_session : NakamaSession, p_channel_id : String, limit : int = 1,
+ forward : bool = true, cursor = null): # -> NakamaAPI.ApiChannelMessageList:
+ return await _api_client.list_channel_messages_async(p_session, p_channel_id, limit, forward, cursor)
+
+# List of friends of the current user.
+# @param p_session - The session of the user.
+# @param p_state - Filter by friendship state.
+# @param p_limit - The number of friends to list.
+# @param p_cursor - A cursor for the current position in the friends list.
+# Returns a task which resolves to the friend objects.
+func list_friends_async(p_session : NakamaSession, p_state = null, p_limit = null, p_cursor = null): # -> NakamaAPI.ApiFriendList:
+ return await _api_client.list_friends_async(p_session, p_limit, p_state, p_cursor)
+
+# List all users part of the group.
+# @param p_session - The session of the user.
+# @param p_group_id - The ID of the group.
+# @param p_state - Filter by group membership state.
+# @param p_limit - The number of groups to list.
+# @param p_cursor - A cursor for the current position in the group listing.
+# Returns a task which resolves to the group user objects.
+func list_group_users_async(p_session : NakamaSession, p_group_id : String, p_state = null, p_limit = null, p_cursor = null): # -> NakamaAPI.ApiGroupUserList:
+ return await _api_client.list_group_users_async(p_session, p_group_id, p_limit, p_state, p_cursor)
+
+# List groups on the server.
+# @param p_session - The session of the user.
+# @param p_name - The name filter to apply to the group list.
+# @param p_limit - The number of groups to list.
+# @param p_cursor - A cursor for the current position in the groups to list.
+# @param p_lang_tag - The language tag filter.
+# @param p_members - The number of group members filter.
+# @param p_open - Optional open/closed filter.
+# Returns a task to resolve group objects.
+func list_groups_async(p_session : NakamaSession, p_name = null, p_limit : int = 10, p_cursor = null, p_lang_tag = null, p_members = null, p_open = null): # -> NakamaAPI.ApiGroupList:
+ return await _api_client.list_groups_async(p_session, p_name, p_cursor, p_limit, p_lang_tag, p_members, p_open)
+
+# List records from a leaderboard.
+# @param p_session - The session of the user.
+# @param p_leaderboard_id - The ID of the leaderboard to list.
+# @param p_owner_ids - Record owners to fetch with the list of records.
+# @param p_expiry - Expiry in seconds (since epoch) to begin fetching records from. Optional. 0 means from current time.
+# @param p_limit - The number of records to list.
+# @param p_cursor - A cursor for the current position in the leaderboard records to list.
+# Returns a task which resolves to the leaderboard record objects.
+func list_leaderboard_records_async(p_session : NakamaSession,
+ p_leaderboard_id : String, p_owner_ids = null, p_expiry = null, p_limit : int = 10, p_cursor = null): # -> NakamaAPI.ApiLeaderboardRecordList:
+ return await _api_client.list_leaderboard_records_async(p_session,
+ p_leaderboard_id, p_owner_ids, p_limit, p_cursor, p_expiry)
+
+# List leaderboard records that belong to a user.
+# @param p_session - The session for the user.
+# @param p_leaderboard_id - The ID of the leaderboard to list.
+# @param p_owner_id - The ID of the user to list around.
+# @param p_expiry - Expiry in seconds (since epoch) to begin fetching records from. Optional. 0 means from current time.
+# @param p_limit - The limit of the listings.
+# @param p_cursor - A cursor for the current position in the leaderboard records to list.
+# Returns a task which resolves to the leaderboard record objects.
+func list_leaderboard_records_around_owner_async(p_session : NakamaSession,
+ p_leaderboar_id : String, p_owner_id : String, p_expiry = null, p_limit : int = 10, p_cursor = null): # -> NakamaAPI.ApiLeaderboardRecordList:
+ return await _api_client.list_leaderboard_records_around_owner_async(p_session,
+ p_leaderboar_id, p_owner_id, p_limit, p_expiry, p_cursor)
+
+# Fetch a list of matches active on the server.
+# @param p_session - The session of the user.
+# @param p_min - The minimum number of match participants.
+# @param p_max - The maximum number of match participants.
+# @param p_limit - The number of matches to list.
+# @param p_authoritative - If authoritative matches should be included.
+# @param p_label - The label to filter the match list on.
+# @param p_query - A query for the matches to filter.
+# Returns a task which resolves to the match list object.
+func list_matches_async(p_session : NakamaSession, p_min : int, p_max : int, p_limit : int, p_authoritative : bool,
+ p_label : String, p_query : String): # -> NakamaAPI.ApiMatchList:
+ return await _api_client.list_matches_async(p_session, p_limit, p_authoritative, p_label if p_label else null, p_min, p_max, p_query if p_query else null)
+
+# List notifications for the user with an optional cursor.
+# @param p_session - The session of the user.
+# @param p_limit - The number of notifications to list.
+# @param p_cacheable_cursor - A cursor for the current position in notifications to list.
+# Returns a task to resolve notifications objects.
+func list_notifications_async(p_session : NakamaSession, p_limit : int = 10, p_cacheable_cursor = null): # -> NakamaAPI.ApiNotificationList:
+ return await _api_client.list_notifications_async(p_session, p_limit, p_cacheable_cursor)
+
+# List storage objects in a collection which have public read access.
+# @param p_session - The session of the user.
+# @param p_collection - The collection to list over.
+# @param p_user_id - The id of the user that owns the objects.
+# @param p_limit - The number of objects to list.
+# @param p_cursor - A cursor to paginate over the collection.
+# Returns a task which resolves to the storage object list.
+func list_storage_objects_async(p_session : NakamaSession, p_collection : String, p_user_id : String = "", p_limit : int = 10, p_cursor = null): # -> NakamaAPI.ApiStorageObjectList:
+ return await _api_client.list_storage_objects_async(p_session, p_collection, p_user_id, p_limit, p_cursor)
+
+# List user's subscriptions.
+# @param p_session - The session of the user.
+# @param p_limit - The number of objects to list.
+# @param p_cursor - A cursor to paginate over the collection.
+# Returns a task which resolves to the subscription list.
+func list_subscriptions_async(p_session : NakamaSession, p_limit: int = 10, p_cursor = null): # -> NakamaAPI.ApiSubscriptionList:
+ return await _api_client.list_subscriptions_async(p_session, NakamaAPI.ApiListSubscriptionsRequest.create(
+ NakamaAPI,
+ {
+ "cursor": p_cursor,
+ "limit": p_limit,
+ }
+ ))
+
+# List tournament records around the owner.
+# @param p_session - The session of the user.
+# @param p_tournament_id - The ID of the tournament.
+# @param p_owner_id - The ID of the owner to pivot around.
+# @param p_expiry - Expiry in seconds (since epoch) to begin fetching records from.
+# @param p_limit - The number of records to list.
+# @param p_cursor - An optional cursor for the next page of tournament records.
+# Returns a task which resolves to the tournament record list object.
+func list_tournament_records_around_owner_async(p_session : NakamaSession,
+ p_tournament_id : String, p_owner_id : String, p_limit : int = 10, p_cursor = null, p_expiry = null): # -> NakamaAPI.ApiTournamentRecordList:
+ return await _api_client.list_tournament_records_around_owner_async(p_session, p_tournament_id, p_owner_id, p_limit, p_expiry, p_cursor)
+
+# List records from a tournament.
+# @param p_session - The session of the user.
+# @param p_tournament_id - The ID of the tournament.
+# @param p_owner_ids - The IDs of the record owners to return in the result.
+# @param p_expiry - Expiry in seconds (since epoch) to begin fetching records from.
+# @param p_limit - The number of records to list.
+# @param p_cursor - An optional cursor for the next page of tournament records.
+# Returns a task which resolves to the list of tournament records.
+func list_tournament_records_async(p_session : NakamaSession, p_tournament_id : String,
+ p_owner_ids = null, p_limit : int = 10, p_cursor = null, p_expiry = null): # -> NakamaAPI.ApiTournamentRecordList:
+ return await _api_client.list_tournament_records_async(p_session, p_tournament_id, p_owner_ids, p_limit, p_cursor, p_expiry)
+
+# List current or upcoming tournaments.
+# @param p_session - The session of the user.
+# @param p_category_start - The start of the category of tournaments to include.
+# @param p_category_end - The end of the category of tournaments to include.
+# @param p_start_time - The start time of the tournaments. (UNIX timestamp)
+# @param p_end_time - The end time of the tournaments. (UNIX timestamp)
+# @param p_limit - The number of tournaments to list.
+# @param p_cursor - An optional cursor for the next page of tournaments.
+# Returns a task which resolves to the list of tournament objects.
+func list_tournaments_async(p_session : NakamaSession, p_category_start : int, p_category_end : int,
+ p_start_time : int, p_end_time : int, p_limit : int = 10, p_cursor = null): # -> NakamaAPI.ApiTournamentList:
+ return await _api_client.list_tournaments_async(p_session,
+ p_category_start, p_category_end, p_start_time, p_end_time, p_limit, p_cursor)
+
+# List of groups the current user is a member of.
+# @param p_session - The session of the user.
+# @param p_user_id - The ID of the user whose groups to list.
+# @param p_state - Filter by group membership state.
+# @param p_limit - The number of records to list.
+# @param p_cursor - A cursor for the current position in the listing.
+# Returns a task which resolves to the group list object.
+func list_user_groups_async(p_session : NakamaSession, p_user_id : String, p_state = null, p_limit = null, p_cursor = null): # -> NakamaAPI.ApiUserGroupList:
+ return await _api_client.list_user_groups_async(p_session, p_user_id, p_limit, p_state, p_cursor)
+
+# List storage objects in a collection which belong to a specific user and have public read access.
+# @param p_session - The session of the user.
+# @param p_collection - The collection to list over.
+# @param p_user_id - The user ID of the user to list objects for.
+# @param p_limit - The number of objects to list.
+# @param p_cursor - A cursor to paginate over the collection.
+# Returns a task which resolves to the storage object list.
+func list_users_storage_objects_async(p_session : NakamaSession,
+ p_collection : String, p_user_id : String, p_limit : int, p_cursor : String): # -> NakamaAPI.ApiStorageObjectList:
+ return await _api_client.list_storage_objects2_async(p_session, p_collection, p_user_id, p_limit, p_cursor)
+
+# Promote one or more users in the group.
+# @param p_session - The session of the user.
+# @param p_group_id - The ID of the group to promote users into.
+# @param p_ids - The IDs of the users to promote.
+# Returns a task which represents the asynchronous operation.
+func promote_group_users_async(p_session : NakamaSession, p_group_id : String, p_ids : PackedStringArray) -> NakamaAsyncResult:
+ return await _api_client.promote_group_users_async(p_session, p_group_id, p_ids)
+
+# Read one or more objects from the storage engine.
+# @param p_session - The session of the user.
+# @param p_ids - The objects to read.
+# Returns a task which resolves to the storage batch object.
+func read_storage_objects_async(p_session : NakamaSession, p_ids : Array): # -> NakamaAPI.ApiStorageObjects:
+ var ids = []
+ for id in p_ids:
+ if not id is NakamaStorageObjectId:
+ continue # TODO Exceptions
+ var obj_id : NakamaStorageObjectId = id
+ ids.append(obj_id.as_read().serialize())
+ return await _api_client.read_storage_objects_async(p_session,
+ NakamaAPI.ApiReadStorageObjectsRequest.create(NakamaAPI, {
+ "object_ids": ids
+ }))
+
+# Execute a function with an input payload on the server.
+# @param p_session - The session of the user.
+# @param p_id - The ID of the function to execute on the server.
+# @param p_payload - The payload to send with the function call.
+# Returns a task which resolves to the RPC response.
+func rpc_async(p_session : NakamaSession, p_id : String, p_payload = null): # -> NakamaAPI.ApiRpc:
+ if p_payload == null:
+ return await _api_client.rpc_func2_async(p_session.token, p_id)
+ return await _api_client.rpc_func_async(p_session.token, p_id, p_payload)
+
+# Execute a function on the server without a session.
+# This function is usually used with server side code. DO NOT USE client side.
+# @param p_http_key - The secure HTTP key used to authenticate.
+# @param p_id - The id of the function to execute on the server.
+# @param p_payload - A payload to send with the function call.
+# Returns a task to resolve an RPC response.
+func rpc_async_with_key(p_http_key : String, p_id : String, p_payload = null): # -> NakamaAPI.ApiRpc:
+ if p_payload == null:
+ return await _api_client.rpc_func2_async("", p_id, null, p_http_key)
+ return await _api_client.rpc_func_async("", p_id, p_payload, p_http_key)
+
+# Log out a session which optionally invalidates the authorization and/or refresh tokens.
+# @param p_session - The session of the user.
+# Returns a task which represents the asynchronous operation.
+func session_logout_async(p_session : NakamaSession) -> NakamaAsyncResult:
+ return await _api_client.session_logout_async(p_session,
+ NakamaAPI.ApiSessionLogoutRequest.create(NakamaAPI, {
+ "refresh_token": p_session.refresh_token,
+ "token": p_session.token
+ }))
+
+# Refresh the session unless the current refresh token has expired. If vars are specified they will replace
+# what is currently stored inside the session token.
+# @param p_session - The session of the user.
+# @param p_vars - Extra information which should be bundled inside the session token.
+# Returns a task which resolves to a new session object.
+func session_refresh_async(p_sesison : NakamaSession, p_vars = null) -> NakamaSession:
+ return _parse_auth(await _api_client.session_refresh_async(server_key, "",
+ NakamaAPI.ApiSessionRefreshRequest.create(NakamaAPI, {
+ "token": p_sesison.refresh_token,
+ "vars": p_vars
+ })))
+
+# Remove the Apple ID from the social profiles on the current user's account.
+# @param p_session - The session of the user.
+# @param p_token - The ID token received from Apple.
+# Returns a task which represents the asynchronous operation.
+func unlink_apple_async(p_session : NakamaSession, p_token : String) -> NakamaAsyncResult:
+ return await _api_client.unlink_apple_async(p_session, NakamaAPI.ApiAccountApple.create(NakamaAPI, {
+ "token": p_token
+ }))
+
+# Unlink a custom ID from the user account owned by the session.
+# @param p_session - The session of the user.
+# @param p_id - A custom identifier usually obtained from an external authentication service.
+# Returns a task which represents the asynchronous operation.
+func unlink_custom_async(p_session : NakamaSession, p_id : String) -> NakamaAsyncResult:
+ return await _api_client.unlink_custom_async(p_session, NakamaAPI.ApiAccountCustom.create(NakamaAPI, {
+ "id": p_id
+ }))
+
+# Unlink a device ID from the user account owned by the session.
+# @param p_session - The session of the user.
+# @param p_id - A device identifier usually obtained from a platform API.
+# Returns a task which represents the asynchronous operation.
+func unlink_device_async(p_session : NakamaSession, p_id : String) -> NakamaAsyncResult:
+ return await _api_client.unlink_device_async(p_session, NakamaAPI.ApiAccountDevice.create(NakamaAPI, {
+ "id": p_id
+ }))
+
+# Unlink an email with password from the user account owned by the session.
+# @param p_session - The session of the user.
+# @param p_email - The email address of the user.
+# @param p_password - The password for the user.
+# Returns a task which represents the asynchronous operation.
+func unlink_email_async(p_session : NakamaSession, p_email : String, p_password : String) -> NakamaAsyncResult:
+ return await _api_client.unlink_email_async(p_session, NakamaAPI.ApiAccountEmail.create(NakamaAPI, {
+ "email": p_email,
+ "password": p_password
+ }))
+
+# Unlink a Facebook profile from the user account owned by the session.
+# @param p_session - The session of the user.
+# @param p_token - An OAuth access token from the Facebook SDK.
+# Returns a task which represents the asynchronous operation.
+func unlink_facebook_async(p_session : NakamaSession, p_token : String) -> NakamaAsyncResult:
+ return await _api_client.unlink_facebook_async(p_session, NakamaAPI.ApiAccountFacebook.create(NakamaAPI, {
+ "token": p_token
+ }))
+
+# Unlink a Facebook profile from the user account owned by the session.
+# @param p_session - The session of the user.
+# @param p_token - An OAuth access token from the Facebook SDK.
+# Returns a task which represents the asynchronous operation.
+func unlink_facebook_instant_game_async(p_session : NakamaSession, p_signed_player_info : String) -> NakamaAsyncResult:
+ return await _api_client.unlink_facebook_instant_game_async(
+ p_session,
+ NakamaAPI.ApiAccountFacebookInstantGame.create(NakamaAPI, {
+ "signed_player_info": p_signed_player_info
+ })
+ )
+
+# Unlink a Game Center profile from the user account owned by the session.
+# @param p_session - The session of the user.
+# @param p_bundle_id - The bundle ID of the Game Center application.
+# @param p_player_id - The player ID of the user in Game Center.
+# @param p_public_key_url - The URL for the public encryption key.
+# @param p_salt - A random `NSString` used to compute the hash and keep it randomized.
+# @param p_signature - The verification signature data generated.
+# @param p_timestamp_seconds - The date and time that the signature was created.
+# Returns a task which represents the asynchronous operation.
+func unlink_game_center_async(p_session : NakamaSession,
+ p_bundle_id : String, p_player_id : String, p_public_key_url : String, p_salt : String, p_signature : String, p_timestamp_seconds) -> NakamaAsyncResult:
+ return await _api_client.unlink_game_center_async(p_session,
+ NakamaAPI.ApiAccountGameCenter.create(NakamaAPI, {
+ "bundle_id": p_bundle_id,
+ "player_id": p_player_id,
+ "public_key_url": p_public_key_url,
+ "salt": p_salt,
+ "signature": p_signature,
+ "timestamp_seconds": p_timestamp_seconds,
+ }))
+
+# Unlink a Google profile from the user account owned by the session.
+# @param p_session - The session of the user.
+# @param p_token - An OAuth access token from the Google SDK.
+# Returns a task which represents the asynchronous operation.
+func unlink_google_async(p_session : NakamaSession, p_token : String) -> NakamaAsyncResult:
+ return await _api_client.unlink_google_async(p_session, NakamaAPI.ApiAccountGoogle.create(NakamaAPI, {
+ "token": p_token
+ }))
+
+# Unlink a Steam profile from the user account owned by the session.
+# @param p_session - The session of the user.
+# @param p_token - An authentication token from the Steam network.
+# Returns a task which represents the asynchronous operation.
+func unlink_steam_async(p_session : NakamaSession, p_token : String) -> NakamaAsyncResult:
+ return await _api_client.unlink_steam_async(p_session, NakamaAPI.ApiAccountSteam.create(NakamaAPI, {
+ "token": p_token
+ }))
+
+# Update the current user's account on the server.
+# @param p_session - The session for the user.
+# @param p_username - The new username for the user.
+# @param p_display_name - A new display name for the user.
+# @param p_avatar_url - A new avatar url for the user.
+# @param p_lang_tag - A new language tag in BCP-47 format for the user.
+# @param p_location - A new location for the user.
+# @param p_timezone - New timezone information for the user.
+# Returns a task which represents the asynchronous operation.
+func update_account_async(p_session : NakamaSession, p_username = null, p_display_name = null,
+ p_avatar_url = null, p_lang_tag = null, p_location = null, p_timezone = null) -> NakamaAsyncResult:
+ return await _api_client.update_account_async(p_session,
+ NakamaAPI.ApiUpdateAccountRequest.create(NakamaAPI, {
+ "avatar_url": p_avatar_url,
+ "display_name": p_display_name,
+ "lang_tag": p_lang_tag,
+ "location": p_location,
+ "timezone": p_timezone,
+ "username": p_username
+ }))
+
+# Update a group.
+# The user must have the correct access permissions for the group.
+# @param p_session - The session of the user.
+# @param p_group_id - The ID of the group to update.
+# @param p_name - A new name for the group.
+# @param p_open - If the group should have open membership.
+# @param p_description - A new description for the group.
+# @param p_avatar_url - A new avatar url for the group.
+# @param p_lang_tag - A new language tag in BCP-47 format for the group.
+# Returns a task which represents the asynchronous operation.
+func update_group_async(p_session : NakamaSession,
+ p_group_id : String, p_name = null, p_description = null, p_avatar_url = null, p_lang_tag = null, p_open = null) -> NakamaAsyncResult:
+ return await _api_client.update_group_async(p_session, p_group_id,
+ NakamaAPI.ApiUpdateGroupRequest.create(NakamaAPI, {
+ "name": p_name,
+ "open": p_open,
+ "avatar_url": p_avatar_url,
+ "description": p_description,
+ "lang_tag": p_lang_tag
+ }))
+
+# Validate a purchase receipt against the Apple App Store.
+# @param p_session - The session of the user.
+# @param p_receipt - The purchase receipt to be validated.
+# Returns a task which resolves to the validated list of purchase receipts.
+func validate_purchase_apple_async(p_session : NakamaSession, p_receipt : String): # -> NakamaAPI.ApiValidatePurchaseResponse
+ return await _api_client.validate_purchase_apple_async(p_session,
+ NakamaAPI.ApiValidatePurchaseAppleRequest.create(NakamaAPI, {
+ "receipt": p_receipt
+ }))
+
+# Validate a purchase receipt against the Google Play Store.
+# @param p_session - The session of the user.
+# @param p_receipt - The purchase receipt to be validated.
+# Returns a task which resolves to the validated list of purchase receipts.
+func validate_purchase_google_async(p_session : NakamaSession, p_receipt : String): # -> NakamaAPI.ApiValidatePurchaseResponse
+ return await _api_client.validate_purchase_google_async(p_session,
+ NakamaAPI.ApiValidatePurchaseGoogleRequest.create(NakamaAPI, {
+ "purchase": p_receipt
+ }))
+
+# Validate a purchase receipt against the Huawei AppGallery.
+# @param p_session - The session of the user.
+# @param p_receipt - The purchase receipt to be validated.
+# @param p_signature - The signature of the purchase receipt.
+# Returns a task which resolves to the validated list of purchase receipts.
+func validate_purchase_huawei_async(p_session : NakamaSession, p_receipt : String, p_signature : String): # -> NakamaAPI.ApiValidatePurchaseResponse
+ return await _api_client.validate_purchase_huawei_async(p_session,
+ NakamaAPI.ApiValidatePurchaseHuaweiRequest.create(NakamaAPI, {
+ "purchase": p_receipt,
+ "signature": p_signature
+ }))
+
+# Validate Apple Subscription Receipt
+# @param p_session - The session of the user.
+# @param p_receipt - The purchase receipt to be validated.
+# @param p_persist - Whether or not to track the receipt in the Nakama database.
+# Returns a task which resolves to the validated subscription response.
+func validate_subscription_apple_async(p_session : NakamaSession, p_receipt : String, p_persist : bool = true): # -> NakamaAPI.ApiValidateSubscriptionResponse:
+ return await _api_client.validate_subscription_apple_async(p_session,
+ NakamaAPI.ApiValidateSubscriptionAppleRequest.create(NakamaAPI, {
+ "receipt": p_receipt,
+ "persist": p_persist,
+ }))
+
+# Validate Google Subscription Receipt
+# @param p_session - The session of the user.
+# @param p_receipt - The purchase receipt to be validated.
+# @param p_persist - Whether or not to track the receipt in the Nakama database.
+# Returns a task which resolves to the validated subscription response.
+func validate_subscription_google_async(p_session : NakamaSession, p_receipt : String, p_persist : bool = true): # -> NakamaAPI.ApiValidateSubscriptionResponse:
+ return await _api_client.validate_subscription_google_async(p_session,
+ NakamaAPI.ApiValidateSubscriptionGoogleRequest.create(NakamaAPI, {
+ "receipt": p_receipt,
+ "persist": p_persist,
+ }))
+
+# Write a record to a leaderboard.
+# @param p_session - The session for the user.
+# @param p_leaderboard_id - The ID of the leaderboard to write.
+# @param p_score - The score for the leaderboard record.
+# @param p_subscore - The subscore for the leaderboard record.
+# @param p_metadata - The metadata for the leaderboard record.
+# Returns a task which resolves to the leaderboard record object written.
+func write_leaderboard_record_async(p_session : NakamaSession,
+ p_leaderboard_id : String, p_score : int, p_subscore : int = 0, p_metadata = null): # -> NakamaAPI.ApiLeaderboardRecord:
+ return await _api_client.write_leaderboard_record_async(p_session, p_leaderboard_id,
+ NakamaAPI.WriteLeaderboardRecordRequestLeaderboardRecordWrite.create(NakamaAPI, {
+ "metadata": p_metadata,
+ "score": str(p_score),
+ "subscore": str(p_subscore)
+ }))
+
+# Write objects to the storage engine.
+# @param p_session - The session of the user.
+# @param p_objects - The objects to write.
+# Returns a task which resolves to the storage write acknowledgements.
+func write_storage_objects_async(p_session : NakamaSession, p_objects : Array): # -> NakamaAPI.ApiStorageObjectAcks:
+ var writes : Array = []
+ for obj in p_objects:
+ if not obj is NakamaWriteStorageObject:
+ continue # TODO Exceptions
+ var write_obj : NakamaWriteStorageObject = obj
+ writes.append(write_obj.as_write().serialize())
+ return await _api_client.write_storage_objects_async(p_session,
+ NakamaAPI.ApiWriteStorageObjectsRequest.create(NakamaAPI, {
+ "objects": writes
+ }))
+
+# Write a record to a tournament.
+# @param p_session - The session of the user.
+# @param p_tournament_id - The ID of the tournament to write.
+# @param p_score - The score of the tournament record.
+# @param p_subscore - The subscore for the tournament record.
+# @param p_metadata - The metadata for the tournament record.
+# Returns a task which resolves to the tournament record object written.
+func write_tournament_record_async(p_session : NakamaSession,
+ p_tournament_id : String, p_score : int, p_subscore : int = 0, p_metadata = null): # -> NakamaAPI.ApiLeaderboardRecord:
+ return await _api_client.write_tournament_record_async(p_session, p_tournament_id,
+ NakamaAPI.WriteTournamentRecordRequestTournamentRecordWrite.create(NakamaAPI, {
+ "metadata": p_metadata,
+ "score": str(p_score),
+ "subscore": str(p_subscore)
+ }))
+
+# Write a record to a tournament.
+# @param p_session - The session of the user.
+# @param p_tournament_id - The ID of the tournament to write.
+# @param p_score - The score of the tournament record.
+# @param p_subscore - The subscore for the tournament record.
+# @param p_metadata - The metadata for the tournament record.
+# Returns a task which resolves to the tournament record object written.
+func write_tournament_record2_async(p_session : NakamaSession,
+ p_tournament_id : String, p_score : int, p_subscore : int = 0, p_metadata = null): # -> NakamaAPI.ApiLeaderboardRecord:
+ return await _api_client.write_tournament_record2_async(p_session, p_tournament_id,
+ NakamaAPI.WriteTournamentRecordRequestTournamentRecordWrite.create(NakamaAPI, {
+ "metadata": p_metadata,
+ "score": str(p_score),
+ "subscore": str(p_subscore)
+ }))
+
diff --git a/game/addons/com.heroiclabs.nakama/client/NakamaClient.gd.uid b/game/addons/com.heroiclabs.nakama/client/NakamaClient.gd.uid
new file mode 100644
index 0000000..02722af
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/client/NakamaClient.gd.uid
@@ -0,0 +1 @@
+uid://ddjhos8e5m4ed
diff --git a/game/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd b/game/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd
new file mode 100644
index 0000000..18ae212
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd
@@ -0,0 +1,208 @@
+@tool
+extends Node
+
+# An adapter which implements the HTTP protocol.
+class_name NakamaHTTPAdapter
+
+# The logger to use with the adapter.
+var logger : RefCounted = NakamaLogger.new()
+
+# The timeout for requests
+var timeout : int = 3
+# If request should be automatically retried when a network error occurs.
+var auto_retry : bool = true
+# The maximum number of time a request will be retried when auto_retry is true
+var auto_retry_count : int = 3
+var auto_retry_backoff_base : int = 10
+# Whether or not to use threads when making HTTP requests.
+var use_threads : bool = true
+
+var _pending = {}
+var id : int = 0
+
+class AsyncRequest:
+ var id : int
+ var request : HTTPRequest
+ var uri : String
+ var method : int
+ var headers : PackedStringArray
+ var body : PackedByteArray
+ var retry_count := 3
+ var backoff_time := 10
+ var logger : NakamaLogger
+
+ var cancelled = false
+ var result : int = HTTPRequest.RESULT_NO_RESPONSE
+ var response_code : int = -1
+ var response_body : PackedByteArray
+ var timer : SceneTreeTimer = null
+ var cur_try : int = 1
+ var rng = RandomNumberGenerator.new()
+
+ func _init(p_id : int, p_request : HTTPRequest, p_uri : String,
+ p_method : int, p_headers : PackedStringArray, p_body : PackedByteArray,
+ p_retry_count : int, p_backoff_time : int, p_logger : NakamaLogger):
+ rng.seed = Time.get_ticks_usec()
+ id = p_id
+ request = p_request
+ uri = p_uri
+ method = p_method
+ headers = p_headers
+ body = p_body
+ retry_count = p_retry_count
+ backoff_time = p_backoff_time
+ logger = p_logger
+
+ func should_retry():
+ return cur_try < retry_count and not cancelled
+
+ func retry():
+ var time = pow(backoff_time, cur_try) * rng.randf_range(0.5, 1)
+ logger.debug("Retrying request %d. Tries left: %d. Backoff: %d ms" % [
+ id, retry_count - cur_try, time
+ ])
+ cur_try += 1
+ await backoff(time)
+ if cancelled:
+ return
+ return await make_request()
+
+ func make_request():
+ var err = request.request(uri, headers, method, body.get_string_from_utf8())
+ if err != OK:
+ await request.get_tree().process_frame
+ result = HTTPRequest.RESULT_CANT_CONNECT
+ logger.debug("Request %d failed to start, error: %d" % [id, err])
+ return
+
+ var args = await request.request_completed
+ result = args[0]
+ response_code = args[1]
+ response_body = args[3]
+
+ func backoff(p_time : int):
+ timer = request.get_tree().create_timer(p_time / 1000)
+ await timer.timeout
+ timer = null
+
+ func cancel():
+ cancelled = true
+ request.cancel_request()
+ if timer:
+ timer.time_left = 0
+ else:
+ request.call_deferred("emit_signal", "request_completed", HTTPRequest.RESULT_REQUEST_FAILED, 0, [], [])
+
+ func parse_result():
+ if cancelled:
+ return NakamaException.new("Request cancelled", -1, -1, true)
+ elif result != HTTPRequest.RESULT_SUCCESS:
+ if result == null:
+ result = 0
+ return NakamaException.new("HTTPRequest failed!", result)
+
+ var json = JSON.new()
+
+ var json_error = json.parse(response_body.get_string_from_utf8())
+ if json_error != OK:
+ logger.debug("Unable to parse request %d response. JSON error: %d, response code: %d" % [
+ id, json.error, response_code
+ ])
+ return NakamaException.new("Failed to decode JSON response", response_code)
+
+ var parsed = json.get_data()
+
+ if response_code != HTTPClient.RESPONSE_OK:
+ var error = ""
+ var code = -1
+ if typeof(parsed) == TYPE_DICTIONARY:
+ if "message" in parsed:
+ error = parsed["message"]
+ elif "error" in parsed:
+ error = parsed["error"]
+ else:
+ error = str(parsed)
+ code = parsed["code"] if "code" in parsed else -1
+ else:
+ error = str(parsed)
+ if typeof(error) == TYPE_DICTIONARY:
+ error = JSON.stringify(error)
+ logger.debug("Request %d returned response code: %d, RPC code: %d, error: %s" % [
+ id, response_code, code, error
+ ])
+ return NakamaException.new(error, response_code, code)
+
+ return parsed
+
+
+# Send a HTTP request.
+# @param method - HTTP method to use for this request.
+# @param uri - The fully qualified URI to use.
+# @param headers - Request headers to set.
+# @param body - Request content body to set.
+# @param timeoutSec - Request timeout.
+# Returns a task which resolves to the contents of the response.
+func send_async(p_method : String, p_uri : String, p_headers : Dictionary, p_body : PackedByteArray):
+ var req = HTTPRequest.new()
+ req.timeout = timeout
+ if use_threads and OS.get_name() != 'Web':
+ req.use_threads = true # Threads not available nor needed on the web.
+
+ # Parse method
+ var method = HTTPClient.METHOD_GET
+ if p_method == "POST":
+ method = HTTPClient.METHOD_POST
+ elif p_method == "PUT":
+ method = HTTPClient.METHOD_PUT
+ elif p_method == "DELETE":
+ method = HTTPClient.METHOD_DELETE
+ elif p_method == "HEAD":
+ method = HTTPClient.METHOD_HEAD
+ var headers = PackedStringArray()
+
+ # Parse headers
+ headers.append("Accept: application/json")
+ for k in p_headers:
+ headers.append("%s: %s" % [k, p_headers[k]])
+
+ id += 1
+ var retry = auto_retry_count if auto_retry else 0
+ var backoff = auto_retry_backoff_base
+ _pending[id] = AsyncRequest.new(id, req, p_uri, method, headers, p_body, retry, backoff, logger)
+
+ logger.debug("Sending request [ID: %d, Method: %s, Uri: %s, Headers: %s, Body: %s, Timeout: %d, Retries: %d, Backoff base: %d ms]" % [
+ id, p_method, p_uri, p_headers, p_body.get_string_from_utf8(), timeout, retry, backoff
+ ])
+
+ add_child(req)
+
+ return await _send_async(id, _pending)
+
+func get_last_token():
+ return id
+
+func cancel_request(p_token):
+ if _pending.has(p_token):
+ _pending[p_token].cancel()
+
+static func _clear_request(p_request : AsyncRequest, p_pending : Dictionary, p_id : int):
+ if not p_request.request.is_queued_for_deletion():
+ p_request.logger.debug("Freeing request %d" % p_id)
+ p_request.request.queue_free()
+ p_pending.erase(p_id)
+
+static func _send_async(p_id : int, p_pending : Dictionary):
+
+ var req : AsyncRequest = p_pending[p_id]
+ await req.make_request()
+
+ while req.result != HTTPRequest.RESULT_SUCCESS:
+ req.logger.debug("Request %d failed with result: %d, response code: %d" % [
+ p_id, req.result, req.response_code
+ ])
+ if not req.should_retry():
+ break
+ await req.retry()
+
+ _clear_request(req, p_pending, p_id)
+ return req.parse_result()
diff --git a/game/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd.uid b/game/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd.uid
new file mode 100644
index 0000000..f7f9178
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd.uid
@@ -0,0 +1 @@
+uid://ckwt5qjweo6jh
diff --git a/game/addons/com.heroiclabs.nakama/dotnet-utils/GodotHttpAdapter.cs b/game/addons/com.heroiclabs.nakama/dotnet-utils/GodotHttpAdapter.cs
new file mode 100644
index 0000000..873cdcf
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/dotnet-utils/GodotHttpAdapter.cs
@@ -0,0 +1,112 @@
+// Copyright 2022 The Nakama Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Nakama.TinyJson;
+using Godot;
+
+namespace Nakama {
+
+ /// <summary>
+ /// An HTTP adapter which uses Godot's HTTPRequest node.
+ /// </summary>
+ /// <remarks>
+ /// Note Content-Type header is always set as 'application/json'.
+ /// </remarks>
+ public partial class GodotHttpAdapter : Node, IHttpAdapter {
+
+ /// <inheritdoc cref="IHttpAdapter.Logger"/>
+ public ILogger Logger { get; set; }
+
+ /// <inheritdoc cref="IHttpAdapter.TransientExceptionDelegate"/>
+ public TransientExceptionDelegate TransientExceptionDelegate => IsTransientException;
+
+ /// <inheritdoc cref="IHttpAdapter"/>
+ public async Task<string> SendAsync(string method, Uri uri, IDictionary<string, string> headers,
+ byte[] body, int timeout, CancellationToken? cancellationToken)
+ {
+ var req = new HTTPRequest();
+ req.Timeout = timeout;
+
+ if (OS.GetName() != "HTML5") {
+ req.UseThreads = true;
+ }
+
+ var godot_method = HTTPClient.Method.Get;
+ if (method == "POST") {
+ godot_method = HTTPClient.Method.Post;
+ }
+ else if (method == "PUT") {
+ godot_method = HTTPClient.Method.Put;
+ }
+ else if (method == "DELETE") {
+ godot_method = HTTPClient.Method.Delete;
+ }
+ else if (method == "HEAD") {
+ godot_method = HTTPClient.Method.Head;
+ }
+
+ var headers_array = new String[headers.Count + 1];
+ headers_array[0] = "Accept: application/json";
+ int index = 1;
+ foreach (var item in headers) {
+ headers_array[index] = item.Key + ": " + item.Value;
+ index++;
+ }
+
+ string body_string = body != null ? System.Text.Encoding.UTF8.GetString(body) : "";
+
+ AddChild(req);
+ req.Request(uri.ToString(), headers_array, true, godot_method, body_string);
+
+ Logger?.InfoFormat("Send: method='{0}', uri='{1}', body='{2}'", method, uri, body_string);
+
+ Variant[] resultObjects = await ToSignal(req, "request_completed");
+
+ HTTPRequest.Result result = (HTTPRequest.Result)(long)resultObjects[0];
+ long response_code = (long)resultObjects[1];
+ string response_body = System.Text.Encoding.UTF8.GetString((byte[])resultObjects[3]);
+
+ req.QueueFree();
+
+ Logger?.InfoFormat("Received: status={0}, contents='{1}'", response_code, response_body);
+
+ if (result == HTTPRequest.Result.Success && response_code >= 200 && response_code <= 299) {
+ return response_body;
+ }
+
+ var decoded = response_body.FromJson<Dictionary<string, object>>();
+ string message = decoded.ContainsKey("message") ? decoded["message"].ToString() : string.Empty;
+ int grpcCode = decoded.ContainsKey("code") ? (int) decoded["code"] : -1;
+
+ var exception = new ApiResponseException(response_code, message, grpcCode);
+
+ if (decoded.ContainsKey("error"))
+ {
+ IHttpAdapterUtil.CopyResponseError(this, decoded["error"], exception);
+ }
+
+ throw exception;
+ }
+
+ private static bool IsTransientException(Exception e) {
+ return e is ApiResponseException apiException && (apiException.StatusCode >= 500 || apiException.StatusCode == -1);
+ }
+
+ }
+
+}
diff --git a/game/addons/com.heroiclabs.nakama/dotnet-utils/GodotLogger.cs b/game/addons/com.heroiclabs.nakama/dotnet-utils/GodotLogger.cs
new file mode 100644
index 0000000..5aa7f1e
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/dotnet-utils/GodotLogger.cs
@@ -0,0 +1,79 @@
+// Copyright 2022 The Nakama Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using Godot;
+
+namespace Nakama {
+
+ /// <summary>
+ /// A logger which prints to the Godot console.
+ /// </summary>
+ public class GodotLogger : ILogger {
+
+ /// <summary>
+ /// The log level.
+ /// </summary>
+ public enum LogLevel {
+ NONE,
+ ERROR,
+ WARNING,
+ INFO,
+ DEBUG,
+ }
+
+ private string module;
+ private LogLevel level;
+
+ /// <summary>
+ /// Constructs a GodotLogger.
+ /// </summary>
+ /// <param name="p_module">The label to use for log entries.</param>
+ /// <param name="p_level">The log level (or lower) to print to the console.</param>
+ public GodotLogger(string p_module = "Nakama", LogLevel p_level = LogLevel.ERROR) {
+ module = p_module;
+ level = p_level;
+ }
+
+ /// <inheritdoc cref="ILogger"/>
+ public void ErrorFormat(string format, params object[] args) {
+ if (level >= LogLevel.ERROR) {
+ GD.PrintErr("=== " + module + " : ERROR === " + String.Format(format, args));
+ }
+ }
+
+ /// <inheritdoc cref="ILogger"/>
+ public void WarnFormat(string format, params object[] args) {
+ if (level >= LogLevel.WARNING) {
+ GD.Print("=== " + module + " : WARN === " + String.Format(format, args));
+ }
+ }
+
+ /// <inheritdoc cref="ILogger"/>
+ public void InfoFormat(string format, params object[] args) {
+ if (level >= LogLevel.INFO) {
+ GD.Print("=== " + module + " : INFO === " + String.Format(format, args));
+ }
+ }
+
+ /// <inheritdoc cref="ILogger"/>
+ public void DebugFormat(string format, params object[] args) {
+ if (level >= LogLevel.DEBUG) {
+ GD.Print("=== " + module + " : DEBUG === " + String.Format(format, args));
+ }
+ }
+
+ }
+
+} \ No newline at end of file
diff --git a/game/addons/com.heroiclabs.nakama/dotnet-utils/GodotWebSocketAdapter.cs b/game/addons/com.heroiclabs.nakama/dotnet-utils/GodotWebSocketAdapter.cs
new file mode 100644
index 0000000..8ac17e7
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/dotnet-utils/GodotWebSocketAdapter.cs
@@ -0,0 +1,211 @@
+// Copyright 2022 The Nakama Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Godot;
+
+namespace Nakama
+{
+ /// <summary>
+ /// An exception that is thrown when the WebSocket is unable to connect.
+ /// </summary>
+ public class GodotWebSocketConnectionException : Exception {
+ public GodotWebSocketConnectionException(string message = "WebSocket unable to connect")
+ : base(message) { }
+ }
+
+ /// <summary>
+ /// An exception that is thrown when the WebSocket is unable to send.
+ /// </summary>
+ public class GodotWebSocketSendException : Exception {
+ public GodotWebSocketSendException() : base("Unable to send over WebSocket") { }
+ }
+
+ /// <summary>
+ /// A socket adapter which uses Godot's WebSocketPeer.
+ /// </summary>
+ public partial class GodotWebSocketAdapter : Node, ISocketAdapter
+ {
+ /// <inheritdoc cref="ISocketAdapter.Connected"/>
+ public event Action Connected;
+
+ /// <inheritdoc cref="ISocketAdapter.Closed"/>
+ public event Action Closed;
+
+ /// <inheritdoc cref="ISocketAdapter.ReceivedError"/>
+ public event Action<Exception> ReceivedError;
+
+ /// <inheritdoc cref="ISocketAdapter.Received"/>
+ public event Action<ArraySegment<byte>> Received;
+
+ /// <inheritdoc cref="ISocketAdapter.IsConnected"/>
+ public new bool IsConnected
+ {
+ get
+ {
+ return ws.GetReadyState() == WebSocketPeer.State.Open;
+ }
+ }
+
+ /// <inheritdoc cref="ISocketAdapter.IsConnecting"/>
+ public bool IsConnecting
+ {
+ get
+ {
+ return ws.GetReadyState() == WebSocketPeer.State.Connecting;
+ }
+ }
+
+ private WebSocketPeer ws;
+ private WebSocketPeer.State wsLastState = WebSocketPeer.State.Closed;
+
+ private TaskCompletionSource<bool> connectionSource;
+ private TaskCompletionSource<bool> closeSource;
+ private int connectionTimeout;
+ private double connectionStart;
+
+ /// <summary>
+ /// Constructs a GodotWebSocketAdapter.
+ /// </summary>
+ public GodotWebSocketAdapter()
+ {
+ ws = new WebSocketPeer();
+ }
+
+ /// <inheritdoc cref="ISocketAdaptor.CloseAsync"/>
+ public Task CloseAsync()
+ {
+ if (closeSource == null)
+ {
+ closeSource = new TaskCompletionSource<bool>();
+ }
+ ws.Close();
+ return closeSource.Task;
+ }
+
+ /// <inheritdoc cref="ISocketAdaptor.ConnectAsync"/>
+ public Task ConnectAsync(Uri uri, int timeout)
+ {
+ if (connectionSource != null)
+ {
+ connectionSource.SetException(new GodotWebSocketConnectionException("Connection attempt aborted due to new connection attempt"));
+ connectionSource = null;
+ }
+
+ if (ws.GetReadyState() != WebSocketPeer.State.Closed)
+ {
+ return Task.FromException(new GodotWebSocketConnectionException("Cannot connect until current socket is closed"));
+ }
+
+ connectionTimeout = timeout;
+ connectionStart = Time.GetUnixTimeFromSystem();
+
+ connectionSource = new TaskCompletionSource<bool>();
+
+ var err = ws.ConnectToUrl(uri.ToString());
+ if (err != Error.Ok)
+ {
+ return Task.FromException(new GodotWebSocketConnectionException(String.Format("Error connecting: {0}", Enum.GetName(typeof(Error), err))));
+ }
+
+ wsLastState = WebSocketPeer.State.Closed;
+
+ return connectionSource.Task;
+ }
+
+ /// <inheritdoc cref="ISocketAdaptor.SendAsync"/>
+ public Task SendAsync(ArraySegment<byte> buffer, bool reliable = true, CancellationToken canceller = default)
+ {
+ byte[] temp;
+
+ if (buffer.Offset != 0 || buffer.Count != buffer.Array.Length)
+ {
+ temp = new byte[buffer.Count];
+ Array.Copy(buffer.Array, buffer.Offset, temp, 0, buffer.Count);
+ }
+ else
+ {
+ temp = buffer.Array;
+ }
+
+ var err = ws.Send(temp, WebSocketPeer.WriteMode.Text);
+ if (err == Error.Ok)
+ {
+ return Task.CompletedTask;
+ }
+
+ return Task.FromException(new GodotWebSocketSendException());
+ }
+
+ public override void _Process(double delta)
+ {
+ if (ws.GetReadyState() != WebSocketPeer.State.Closed)
+ {
+ ws.Poll();
+ }
+
+ var state = ws.GetReadyState();
+ if (wsLastState != state)
+ {
+ wsLastState = state;
+
+ if (state == WebSocketPeer.State.Open)
+ {
+ Connected?.Invoke();
+ connectionSource.SetResult(true);
+ connectionSource = null;
+ }
+ else if (state == WebSocketPeer.State.Closed)
+ {
+ if (connectionSource != null)
+ {
+ Exception e = new GodotWebSocketConnectionException("Failed to connect");
+ ReceivedError?.Invoke(e);
+ connectionSource.SetException(e);
+ connectionSource = null;
+ }
+ else
+ {
+ Closed?.Invoke();
+ }
+
+ if (closeSource != null)
+ {
+ closeSource.SetResult(true);
+ closeSource = null;
+ }
+ }
+ }
+
+ if (ws.GetReadyState() == WebSocketPeer.State.Connecting) {
+ if (connectionStart + (double)connectionTimeout < Time.GetUnixTimeFromSystem())
+ {
+ ws.Close();
+
+ Exception e = new GodotWebSocketConnectionException("Connection timed out");
+ ReceivedError?.Invoke(e);
+ connectionSource.SetException(e);
+ connectionSource = null;
+ }
+ }
+
+ while (ws.GetReadyState() == WebSocketPeer.State.Open && ws.GetAvailablePacketCount() > 0)
+ {
+ Received?.Invoke(new ArraySegment<byte>(ws.GetPacket()));
+ }
+ }
+ }
+}
diff --git a/game/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd b/game/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd
new file mode 100644
index 0000000..f0b9416
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd
@@ -0,0 +1,598 @@
+extends RefCounted
+
+# A socket to interact with Nakama server.
+class_name NakamaSocket
+
+const ChannelType = NakamaRTMessage.ChannelJoin.ChannelType
+
+# Emitted when a socket is closed.
+signal closed()
+
+# Emitted when a socket is connected.
+signal connected()
+
+# Emitted when an error occurs while connecting.
+signal connection_error(p_error)
+
+# Emitted when a chat channel message is received
+signal received_channel_message(p_channel_message) # ApiChannelMessage
+
+# Emitted when receiving a presence change for joins and leaves with users in a chat channel.
+signal received_channel_presence(p_channel_presence) # ChannelPresenceEvent
+
+# Emitted when an error is received from the server.
+signal received_error(p_error) # Error
+
+# Emitted when receiving a matchmaker matched message.
+signal received_matchmaker_matched(p_matchmaker_matched) # MatchmakerMatched
+
+# Emitted when receiving a message from a multiplayer match.
+signal received_match_state(p_match_state) # MatchData
+
+# Emitted when receiving a presence change for joins and leaves of users in a multiplayer match.
+signal received_match_presence(p_match_presence_event) # MatchPresenceEvent
+
+# Emitted when receiving a notification for the current user.
+signal received_notification(p_api_notification) # ApiNotification
+
+# Emitted when receiving a presence change for when a user updated their online status.
+signal received_status_presence(p_status_presence_event) # StatusPresenceEvent
+
+# Emitted when receiving a presence change for joins and leaves on a realtime stream.
+signal received_stream_presence(p_stream_presence_event) # StreamPresenceEvent
+
+# Emitted when receiving a message from a realtime stream.
+signal received_stream_state(p_stream_state) # StreamState
+
+# Received a party event. This will occur when the current user's invitation request is accepted
+# the party leader of a closed party.
+signal received_party(p_party) # Party
+
+# Received a party close event.
+signal received_party_close(p_party_close) # PartyClose
+
+# Received custom party data.
+signal received_party_data(p_party_data) # PartyData
+
+# Received a request to join the party.
+signal received_party_join_request(p_party_join_request) # PartyJoinRequest
+
+# Received a change in the party leader.
+signal received_party_leader(p_party_leader) # PartyLeader
+
+# Received a new matchmaker ticket for the party.
+signal received_party_matchmaker_ticket(p_party_matchmaker_ticket) # PartyMatchmakerTicket
+
+# Received a new presence event in the party.
+signal received_party_presence(p_party_presence_event) # PartyPresenceEvent
+
+var _adapter : NakamaSocketAdapter
+var _free_adapter : bool = false
+var _weak_ref : WeakRef
+var _base_uri : String
+var _requests : Dictionary
+var _last_id : int = 1
+var _conn = null
+var logger : NakamaLogger = null
+
+class AsyncConnection:
+ signal completed(result)
+
+ func resume(result) -> void:
+ emit_signal("completed", result)
+
+class AsyncRequest:
+ var id : String
+ var type
+ var ns
+ var result_key : String
+
+ signal completed(result)
+
+ func _init(p_id : String, p_type, p_ns, p_result_key = null):
+ id = p_id
+ type = p_type
+ ns = p_ns
+
+ if type != NakamaAsyncResult:
+ # Specifically defined key, or default for object.
+ result_key = p_result_key if p_result_key != null else type.get_result_key()
+
+ func resume(data, logger = null) -> void:
+ var result = _parse_result(data, logger)
+ emit_signal("completed", result)
+
+ func _parse_result(data, logger):
+ # We got an exception, maybe the task was cancelled?
+ if data is NakamaException:
+ return type.new(data as NakamaException)
+
+ # Error from server
+ if data.has("error"):
+ var err = data["error"]
+ var code = -1
+ var msg = str(err)
+ if typeof(err) == TYPE_DICTIONARY:
+ msg = err.get("message", "")
+ code = err.get("code", -1)
+ if logger:
+ logger.warning("Error response from server: %s" % err)
+ return type.new(NakamaException.new(msg, code))
+ # Simple ack response
+ elif type == NakamaAsyncResult:
+ return NakamaAsyncResult.new()
+ # Missing expected result key
+ elif not data.has(result_key):
+ if logger:
+ logger.warning("Missing expected result key: %s" % result_key)
+ return type.new(NakamaException.new("Missing expected result key: %s" % result_key))
+ # All good, proceed with parsing
+ else:
+ return type.create(ns, data.get(result_key))
+
+func _resume_conn(p_err : int):
+ if _conn:
+ if p_err: # Exception
+ logger.warning("Connection error: %d" % p_err)
+ _conn.resume(NakamaAsyncResult.new(NakamaException.new()))
+ else:
+ logger.info("Connected!")
+ _conn.resume(NakamaAsyncResult.new())
+ _conn = null
+
+func _init(p_adapter : NakamaSocketAdapter,
+ p_host : String,
+ p_port : int,
+ p_scheme : String,
+ p_free_adapter : bool = false):
+ logger = p_adapter.logger
+ _adapter = p_adapter
+ _weak_ref = weakref(_adapter)
+ var port = ""
+ if (p_scheme == "ws" and p_port != 80) or (p_scheme == "wss" and p_port != 443):
+ port = ":%d" % p_port
+ _base_uri = "%s://%s%s" % [p_scheme, p_host, port]
+ _free_adapter = p_free_adapter
+ _adapter.closed.connect(self._closed)
+ _adapter.connected.connect(self._connected)
+ _adapter.received_error.connect(self._connection_error)
+ _adapter.received.connect(self._received)
+
+func _notification(what):
+ if what == NOTIFICATION_PREDELETE:
+ # Is this a bug? Why can't I call a function? self is null...
+# _clear_responses()
+# _resume_conn(ERR_FILE_EOF)
+ var keys = _requests.keys()
+ for k in keys:
+ _requests[k].resume(NakamaException.new("Cancelled!"))
+ if _conn != null:
+ _conn.resume(ERR_FILE_EOF)
+ _conn = null
+ if _weak_ref.get_ref() == null:
+ return
+ _adapter.close()
+ if _free_adapter:
+ _adapter.queue_free()
+
+func _closed(p_error = null):
+ emit_signal("closed")
+ _resume_conn(ERR_CANT_CONNECT)
+ _clear_requests()
+
+func _connection_error(p_error):
+ emit_signal("connection_error", p_error)
+ _resume_conn(p_error)
+ _clear_requests()
+
+func _connected():
+ emit_signal("connected")
+ _resume_conn(OK)
+
+func _received(p_bytes : PackedByteArray):
+ var json = JSON.new()
+ var json_str = p_bytes.get_string_from_utf8()
+ var json_error := json.parse(json_str)
+ if json_error != OK or typeof(json.get_data()) != TYPE_DICTIONARY:
+ logger.error("Unable to parse response: %s" % json_str)
+ return
+ var dict : Dictionary = json.get_data()
+ var cid = dict.get("cid")
+ if cid:
+ if _requests.has(cid):
+ _resume_request(cid, dict)
+ else:
+ logger.error("Invalid call id received %s" % dict)
+ else:
+ if dict.has("error"):
+ var res = NakamaRTAPI.Error.create(NakamaRTAPI, dict["error"])
+ emit_signal("received_error", res)
+ elif dict.has("channel_message"):
+ var res = NakamaAPI.ApiChannelMessage.create(NakamaAPI, dict["channel_message"])
+ emit_signal("received_channel_message", res)
+ elif dict.has("channel_presence_event"):
+ var res = NakamaRTAPI.ChannelPresenceEvent.create(NakamaRTAPI, dict["channel_presence_event"])
+ emit_signal("received_channel_presence", res)
+ elif dict.has("match_data"):
+ var res = NakamaRTAPI.MatchData.create(NakamaRTAPI, dict["match_data"])
+ emit_signal("received_match_state", res)
+ elif dict.has("match_presence_event"):
+ var res = NakamaRTAPI.MatchPresenceEvent.create(NakamaRTAPI, dict["match_presence_event"])
+ emit_signal("received_match_presence", res)
+ elif dict.has("matchmaker_matched"):
+ var res = NakamaRTAPI.MatchmakerMatched.create(NakamaRTAPI, dict["matchmaker_matched"])
+ emit_signal("received_matchmaker_matched", res)
+ elif dict.has("notifications"):
+ var res = NakamaAPI.ApiNotificationList.create(NakamaAPI, dict["notifications"])
+ for n in res.notifications:
+ emit_signal("received_notification", n)
+ elif dict.has("status_presence_event"):
+ var res = NakamaRTAPI.StatusPresenceEvent.create(NakamaRTAPI, dict["status_presence_event"])
+ emit_signal("received_status_presence", res)
+ elif dict.has("stream_presence_event"):
+ var res = NakamaRTAPI.StreamPresenceEvent.create(NakamaRTAPI, dict["stream_presence_event"])
+ emit_signal("received_stream_presence", res)
+ elif dict.has("stream_data"):
+ var res = NakamaRTAPI.StreamData.create(NakamaRTAPI, dict["stream_data"])
+ emit_signal("received_stream_state", res)
+ elif dict.has("party"):
+ var res = NakamaRTAPI.Party.create(NakamaRTAPI, dict["party"])
+ emit_signal("received_party", res)
+ elif dict.has("party_close"):
+ var res = NakamaRTAPI.PartyClose.create(NakamaRTAPI, dict["party_close"])
+ emit_signal("received_party_close", res)
+ elif dict.has("party_data"):
+ var res = NakamaRTAPI.PartyData.create(NakamaRTAPI, dict["party_data"])
+ emit_signal("received_party_data", res)
+ elif dict.has("party_join_request"):
+ var res = NakamaRTAPI.PartyJoinRequest.create(NakamaRTAPI, dict["party_join_request"])
+ emit_signal("received_party_join_request", res)
+ elif dict.has("party_leader"):
+ var res = NakamaRTAPI.PartyLeader.create(NakamaRTAPI, dict["party_leader"])
+ emit_signal("received_party_leader", res)
+ elif dict.has("party_matchmaker_ticket"):
+ var res = NakamaRTAPI.PartyMatchmakerTicket.create(NakamaRTAPI, dict["party_matchmaker_ticket"])
+ emit_signal("received_party_matchmaker_ticket", res)
+ elif dict.has("party_presence_event"):
+ var res = NakamaRTAPI.PartyPresenceEvent.create(NakamaRTAPI, dict["party_presence_event"])
+ emit_signal("received_party_presence", res)
+ else:
+ logger.warning("Unhandled response: %s" % dict)
+
+func _resume_request(p_id : String, p_data):
+ if _requests.has(p_id):
+ logger.debug("Resuming request: %s: %s" % [p_id, p_data])
+ _requests[p_id].resume(p_data, logger)
+ _requests.erase(p_id)
+ else:
+ logger.warning("Trying to resume missing request: %s: %s" % [p_id, p_data])
+
+func _cancel_request(p_id : String):
+ logger.debug("Cancelling request: %s" % [p_id])
+ _resume_request(p_id, NakamaException.new("Request cancelled."))
+
+func _clear_requests():
+ var ids = _requests.keys()
+ for id in ids:
+ _cancel_request(id)
+
+func _send_async(p_message, p_parse_type = NakamaAsyncResult, p_ns = NakamaRTAPI, p_msg_key = null, p_result_key = null) -> AsyncRequest:
+ logger.debug("Sending async request: %s" % p_message)
+ # For messages coming from the API which does not have a key defined, so we can override it
+ var msg = p_msg_key
+ # For regular RT messages
+ if msg == null:
+ msg = p_message.get_msg_key()
+ var id = str(_last_id)
+ _last_id += 1
+
+ _requests[id] = AsyncRequest.new(id, p_parse_type, p_ns, p_result_key)
+
+ var json := JSON.stringify({
+ "cid": id,
+ msg: p_message.serialize()
+ })
+ var err = _adapter.send(json.to_utf8_buffer())
+ if err != OK:
+ call_deferred("_cancel_request", id)
+ return _requests[id]
+
+# If the socket is connected.
+func is_connected_to_host():
+ return _adapter.is_connected_to_host()
+
+# If the socket is connecting.
+func is_connecting_to_host():
+ return _adapter.is_connecting_to_host()
+
+# Close the socket connection to the server.
+func close():
+ _adapter.close()
+
+# Connect to the server.
+# @param p_session - The session of the user.
+# @param p_appear_online - If the user who appear online to other users.
+# @param p_connect_timeout - The time allowed for the socket connection to be established.
+# Returns a task to represent the asynchronous operation.
+func connect_async(p_session : NakamaSession, p_appear_online : bool = false, p_connect_timeout : int = 3):
+ var uri = "%s/ws?lang=en&status=%s&token=%s" % [_base_uri, str(p_appear_online).to_lower(), p_session.token]
+ logger.debug("Connecting to host: %s" % uri)
+ _conn = AsyncConnection.new()
+ _adapter.connect_to_host(uri, p_connect_timeout)
+ return await _conn.completed
+
+# Join the matchmaker pool and search for opponents on the server.
+# @param p_query - The matchmaker query to search for opponents.
+# @param p_min_count - The minimum number of players to compete against in a match.
+# @param p_max_count - The maximum number of players to compete against in a match.
+# @param p_string_properties - A set of key/value properties to provide to searches.
+# @param p_numeric_properties - A set of key/value numeric properties to provide to searches.
+# @param p_count_multiple - Optional multiple of the count that must be satisfied.
+# Returns a task which resolves to a matchmaker ticket object.
+func add_matchmaker_async(p_query : String = "*", p_min_count : int = 2, p_max_count : int = 8,
+ p_string_props : Dictionary = {}, p_numeric_props : Dictionary = {},
+ p_count_multiple : int = 0) -> NakamaRTAPI.MatchmakerTicket:
+ return await _send_async(
+ NakamaRTMessage.MatchmakerAdd.new(p_query, p_min_count, p_max_count, p_string_props, p_numeric_props, p_count_multiple),
+ NakamaRTAPI.MatchmakerTicket
+ ).completed
+
+# Create a multiplayer match on the server.
+# @param p_name - Optional name to use when creating the match.
+# Returns a task to represent the asynchronous operation.
+func create_match_async(p_name : String = ''):
+ return await _send_async(NakamaRTMessage.MatchCreate.new(p_name), NakamaRTAPI.Match).completed
+
+# Subscribe to one or more users for their status updates.
+# @param p_user_ids - The IDs of users.
+# @param p_usernames - The usernames of the users.
+# Returns a task which resolves to the current statuses for the users.
+func follow_users_async(p_ids : PackedStringArray, p_usernames : PackedStringArray) -> NakamaRTAPI.Status:
+ return await _send_async(NakamaRTMessage.StatusFollow.new(p_ids, p_usernames), NakamaRTAPI.Status).completed
+
+# Join a chat channel on the server.
+# @param p_target - The target channel to join.
+# @param p_type - The type of channel to join.
+# @param p_persistence - If chat messages should be stored.
+# @param p_hidden - If the current user should be hidden on the channel.
+# Returns a task which resolves to a chat channel object.
+func join_chat_async(p_target : String, p_type : int, p_persistence : bool = false, p_hidden : bool = false) -> NakamaRTAPI.Channel:
+ return await _send_async(
+ NakamaRTMessage.ChannelJoin.new(p_target, p_type, p_persistence, p_hidden),
+ NakamaRTAPI.Channel
+ ).completed
+
+# Join a multiplayer match with the matchmaker matched object.
+# @param p_matched - A matchmaker matched object.
+# Returns a task which resolves to a multiplayer match.
+func join_matched_async(p_matched):
+ var msg := NakamaRTMessage.MatchJoin.new()
+ if p_matched.match_id:
+ msg.match_id = p_matched.match_id
+ else:
+ msg.token = p_matched.token
+ return await _send_async(msg, NakamaRTAPI.Match).completed
+
+# Join a multiplayer match by ID.
+# @param p_match_id - The ID of the match to attempt to join.
+# @param p_metadata - An optional set of key-value metadata pairs to be passed to the match handler.
+# Returns a task which resolves to a multiplayer match.
+func join_match_async(p_match_id : String, p_metadata = null):
+ var msg := NakamaRTMessage.MatchJoin.new()
+ msg.match_id = p_match_id
+ msg.metadata = p_metadata
+ return await _send_async(msg, NakamaRTAPI.Match).completed
+
+# Leave a chat channel on the server.
+## @param p_channel_id - The ID of the chat channel to leave.
+# Returns a task which represents the asynchronous operation.
+func leave_chat_async(p_channel_id : String) -> NakamaAsyncResult:
+ return await _send_async(NakamaRTMessage.ChannelLeave.new(p_channel_id)).completed
+
+# Leave a multiplayer match on the server.
+# @param p_match_id - The multiplayer match to leave.
+# Returns a task which represents the asynchronous operation.
+func leave_match_async(p_match_id : String) -> NakamaAsyncResult:
+ return await _send_async(NakamaRTMessage.MatchLeave.new(p_match_id)).completed
+
+# Remove a chat message from a chat channel on the server.
+# @param p_channel - The chat channel with the message to remove.
+# @param p_message_id - The ID of the chat message to remove.
+# Returns a task which resolves to an acknowledgement of the removed message.
+func remove_chat_message_async(p_channel_id : String, p_message_id : String):
+ return await _send_async(
+ NakamaRTMessage.ChannelMessageRemove.new(p_channel_id, p_message_id),
+ NakamaRTAPI.ChannelMessageAck
+ ).completed
+
+# Leave the matchmaker pool with the ticket.
+# @param p_ticket - The ticket returned by the matchmaker on join.
+# Returns a task which represents the asynchronous operation.
+func remove_matchmaker_async(p_ticket : String) -> NakamaAsyncResult:
+ return await _send_async(NakamaRTMessage.MatchmakerRemove.new(p_ticket)).completed
+
+# Execute an RPC function to the server.
+# @param p_func_id - The ID of the function to execute.
+# @param p_payload - An (optional) String payload to send to the server.
+# Returns a task which resolves to the RPC function response object.
+func rpc_async(p_func_id : String, p_payload = null) -> NakamaAPI.ApiRpc:
+ var payload = p_payload
+ match typeof(p_payload):
+ TYPE_NIL, TYPE_STRING:
+ pass
+ _:
+ payload = JSON.stringify(p_payload)
+ return await _send_async(NakamaAPI.ApiRpc.create(NakamaAPI, {
+ "id": p_func_id,
+ "payload": payload
+ }), NakamaAPI.ApiRpc, NakamaAPI, "rpc", "rpc").completed
+
+# Send input to a multiplayer match on the server.
+# When no presences are supplied the new match state will be sent to all presences.
+# @param p_match_id - The ID of the match.
+# @param p_op_code - An operation code for the input.
+# @param p_data - The input data to send.
+# @param p_presences - The presences in the match who should receive the input.
+# Returns a task which represents the asynchronous operation.
+func send_match_state_async(p_match_id, p_op_code : int, p_data : String, p_presences = null):
+ var req = _send_async(NakamaRTMessage.MatchDataSend.new(
+ p_match_id,
+ p_op_code,
+ Marshalls.utf8_to_base64(p_data),
+ p_presences
+ ))
+ # This do not return a response from server, you don't really need to wait for it.
+ req.call_deferred("resume", {})
+ return req.completed
+
+# Send input to a multiplayer match on the server.
+# When no presences are supplied the new match state will be sent to all presences.
+# @param p_match_id - The ID of the match.
+# @param p_op_code - An operation code for the input.
+# @param p_data - The input data to send.
+# @param p_presences - The presences in the match who should receive the input.
+# Returns a task which represents the asynchronous operation.
+func send_match_state_raw_async(p_match_id, p_op_code : int, p_data : PackedByteArray, p_presences = null):
+ var req = _send_async(NakamaRTMessage.MatchDataSend.new(
+ p_match_id,
+ p_op_code,
+ Marshalls.raw_to_base64(p_data),
+ p_presences
+ ))
+ # This do not return a response from server, you don't really need to wait for it.
+ req.call_deferred("resume", {})
+ return req.completed
+
+# Unfollow one or more users from their status updates.
+# @param p_user_ids - An array of user ids to unfollow.
+# Returns a task which represents the asynchronous operation.
+func unfollow_users_async(p_ids : PackedStringArray):
+ return await _send_async(NakamaRTMessage.StatusUnfollow.new(p_ids)).completed
+
+# Update a chat message on a chat channel in the server.
+# @param p_channel_id - The ID of the chat channel with the message to update.
+# @param p_message_id - The ID of the message to update.
+# @param p_content - The new contents of the chat message.
+# Returns a task which resolves to an acknowledgement of the updated message.
+func update_chat_message_async(p_channel_id : String, p_message_id : String, p_content : Dictionary):
+ return await _send_async(
+ NakamaRTMessage.ChannelMessageUpdate.new(p_channel_id, p_message_id, JSON.stringify(p_content)),
+ NakamaRTAPI.ChannelMessageAck
+ ).completed
+
+# Update the status for the current user online.
+# @param p_status - The new status for the user.
+# Returns a task which represents the asynchronous operation.
+func update_status_async(p_status : String):
+ return await _send_async(NakamaRTMessage.StatusUpdate.new(p_status)).completed
+
+# Send a chat message to a chat channel on the server.
+# @param p_channel_id - The ID of the chat channel to send onto.
+# @param p_content - The contents of the message to send.
+# Returns a task which resolves to the acknowledgement of the chat message write.
+func write_chat_message_async(p_channel_id : String, p_content : Dictionary):
+ return await _send_async(
+ NakamaRTMessage.ChannelMessageSend.new(p_channel_id, JSON.stringify(p_content)),
+ NakamaRTAPI.ChannelMessageAck
+ ).completed
+
+# Accept a party member's request to join the party.
+# @param p_party_id - The party ID to accept the join request for.
+# @param p_presence - The presence to accept as a party member.
+# Returns a task to represent the asynchronous operation.
+func accept_party_member_async(p_party_id : String, p_presence : NakamaRTAPI.UserPresence):
+ return await _send_async(NakamaRTMessage.PartyAccept.new(p_party_id, p_presence)).completed
+
+# Begin matchmaking as a party.
+# @param p_party_id - Party ID.
+# @param p_query - Filter query used to identify suitable users.
+# @param p_min_count - Minimum total user count to match together.
+# @param p_max_count - Maximum total user count to match together.
+# @param p_string_properties - String properties.
+# @param p_numeric_properties - Numeric properties.
+# @param p_count_multiple - Optional multiple of the count that must be satisfied.
+# Returns a task to represent the asynchronous operation.
+func add_matchmaker_party_async(p_party_id : String, p_query : String = "*", p_min_count : int = 2,
+ p_max_count : int = 8, p_string_properties = {}, p_numeric_properties = {}, p_count_multiple : int = 0):
+ return await _send_async(
+ NakamaRTMessage.PartyMatchmakerAdd.new(p_party_id, p_min_count,
+ p_max_count, p_query, p_string_properties, p_numeric_properties,
+ p_count_multiple if p_count_multiple > 0 else null),
+ NakamaRTAPI.PartyMatchmakerTicket).completed
+
+# End a party, kicking all party members and closing it.
+# @param p_party_id - The ID of the party.
+# Returns a task to represent the asynchronous operation.
+func close_party_async(p_party_id : String):
+ var msg := NakamaRTAPI.PartyClose.new()
+ msg.party_id = p_party_id
+ return await _send_async(msg).completed
+
+# Create a party.
+# @param p_open - Whether or not the party will require join requests to be approved by the party leader.
+# @param p_max_size - Maximum number of party members. This maximum does not include the party leader.
+# Returns a task to represent the asynchronous operation.
+func create_party_async(p_open : bool, p_max_size : int) -> NakamaRTAPI.Party:
+ return await _send_async(
+ NakamaRTMessage.PartyCreate.new(p_open, p_max_size),
+ NakamaRTAPI.Party
+ ).completed
+
+# Join a party.
+# @param p_party_id - Party ID.
+# Returns a task to represent the asynchronous operation.
+func join_party_async(p_party_id : String):
+ return await _send_async(NakamaRTMessage.PartyJoin.new(p_party_id)).completed
+
+# Leave the party.
+# @param p_party_id - Party ID.
+# Returns a task to represent the asynchronous operation.
+func leave_party_async(p_party_id : String):
+ return await _send_async(NakamaRTMessage.PartyLeave.new(p_party_id)).completed
+
+# Request a list of pending join requests for a party.
+# @param p_party_id - Party ID.
+# Returns a task which resolves to a list of all party join requests.
+func list_party_join_requests_async(p_party_id : String) -> NakamaRTAPI.PartyJoinRequest:
+ return await _send_async(
+ NakamaRTMessage.PartyJoinRequestList.new(p_party_id),
+ NakamaRTAPI.PartyJoinRequest).completed
+
+# Promote a new party leader.
+# @param p_party_id - Party ID.
+# @param p_party_member - The presence of an existing party member to promote as the new leader.
+# Returns a which represents the asynchronous operation.
+func promote_party_member(p_party_id : String, p_party_member : NakamaRTAPI.UserPresence):
+ return await _send_async(NakamaRTMessage.PartyPromote.new(p_party_id, p_party_member)).completed
+
+# Cancel a party matchmaking process using a ticket.
+# @param p_party_id - Party ID.
+# @param p_ticket - The ticket to cancel.
+# Returns a task which represents the asynchronous operation.
+func remove_matchmaker_party_async(p_party_id : String, p_ticket : String):
+ return await _send_async(NakamaRTMessage.PartyMatchmakerRemove.new(p_party_id, p_ticket)).completed
+
+# Kick a party member, or decline a request to join.
+# @param p_party_id - Party ID to remove/reject from.
+# @param p_presence - The presence to remove or reject.
+# Returns a task which represents the asynchronous operation.
+func remove_party_member_async(p_party_id : String, p_presence : NakamaRTAPI.UserPresence):
+ return await _send_async(NakamaRTMessage.PartyRemove.new(p_party_id, p_presence)).completed
+
+# Send data to a party.
+# @param p_party_id - Party ID to send to.
+# @param p_op_code - Op code value.
+# @param data - Data payload, if any.
+# Returns a task which represents the asynchronous operation.
+func send_party_data_async(p_party_id : String, p_op_code : int, p_data:String = ""):
+ var base64_data = null if p_data.is_empty() else Marshalls.utf8_to_base64(p_data)
+ return await _send_async(NakamaRTMessage.PartyDataSend.new(p_party_id, p_op_code, base64_data)).completed
+
+# Send data to a party.
+# @param p_party_id - Party ID to send to.
+# @param p_op_code - Op code value.
+# @param data - Data payload, if any.
+# Returns a task which represents the asynchronous operation.
+func send_party_data_raw_async(p_party_id : String, p_op_code : int, p_data:PackedByteArray):
+ var base64_data = null if p_data.is_empty() else Marshalls.raw_to_base64(p_data)
+ return await _send_async(NakamaRTMessage.PartyDataSend.new(p_party_id, p_op_code, base64_data)).completed
diff --git a/game/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd.uid b/game/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd.uid
new file mode 100644
index 0000000..5c2df7a
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd.uid
@@ -0,0 +1 @@
+uid://cdl0qejufpq1r
diff --git a/game/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd b/game/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd
new file mode 100644
index 0000000..6a0ebe6
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd
@@ -0,0 +1,75 @@
+@tool
+extends Node
+
+# An adapter which implements a socket with a protocol supported by Nakama.
+class_name NakamaSocketAdapter
+
+var _ws := WebSocketPeer.new()
+var _ws_last_state := WebSocketPeer.STATE_CLOSED
+var _timeout : int = 30
+var _start : int = 0
+var logger = NakamaLogger.new()
+
+# A signal emitted when the socket is connected.
+signal connected()
+
+# A signal emitted when the socket is disconnected.
+signal closed()
+
+# A signal emitted when the socket has an error when connecting.
+signal received_error(p_exception)
+
+# A signal emitted when the socket receives a message.
+signal received(p_bytes) # PackedByteArray
+
+# If the socket is connected.
+func is_connected_to_host():
+ return _ws.get_ready_state() == WebSocketPeer.STATE_OPEN
+
+# If the socket is connecting.
+func is_connecting_to_host():
+ return _ws.get_ready_state() == WebSocketPeer.STATE_CONNECTING
+
+# Close the socket with an asynchronous operation.
+func close():
+ _ws.close()
+
+# Connect to the server with an asynchronous operation.
+# @param p_uri - The URI of the server.
+# @param p_timeout - The timeout for the connect attempt on the socket.
+func connect_to_host(p_uri : String, p_timeout : int):
+ _timeout = p_timeout
+ _start = Time.get_unix_time_from_system()
+ var err = _ws.connect_to_url(p_uri)
+ if err != OK:
+ logger.debug("Error connecting to host %s" % p_uri)
+ call_deferred("emit_signal", "received_error", err)
+ return
+ _ws_last_state = WebSocketPeer.STATE_CLOSED
+
+# Send data to the server with an asynchronous operation.
+# @param p_buffer - The buffer with the message to send.
+# @param p_reliable - If the message should be sent reliably (will be ignored by some protocols).
+func send(p_buffer : PackedByteArray, p_reliable : bool = true) -> int:
+ return _ws.send(p_buffer, WebSocketPeer.WRITE_MODE_TEXT)
+
+func _process(delta):
+ if _ws.get_ready_state() != WebSocketPeer.STATE_CLOSED:
+ _ws.poll()
+
+ var state = _ws.get_ready_state()
+ if _ws_last_state != state:
+ _ws_last_state = state
+ if state == WebSocketPeer.STATE_OPEN:
+ connected.emit()
+ elif state == WebSocketPeer.STATE_CLOSED:
+ closed.emit()
+
+ if state == WebSocketPeer.STATE_CONNECTING:
+ if _start + _timeout < Time.get_unix_time_from_system():
+ logger.debug("Timeout when connecting to socket")
+ received_error.emit(ERR_TIMEOUT)
+ _ws.close()
+
+ while _ws.get_ready_state() == WebSocketPeer.STATE_OPEN and _ws.get_available_packet_count():
+ received.emit(_ws.get_packet())
diff --git a/game/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd.uid b/game/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd.uid
new file mode 100644
index 0000000..d4d4974
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd.uid
@@ -0,0 +1 @@
+uid://dnjem72iwolrq
diff --git a/game/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd b/game/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd
new file mode 100644
index 0000000..62228ff
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd
@@ -0,0 +1,34 @@
+extends RefCounted
+class_name NakamaAsyncResult
+
+var exception : NakamaException:
+ set(v):
+ pass
+ get:
+ return get_exception()
+
+var _ex = null
+
+func _init(p_ex = null):
+ _ex = p_ex
+
+func is_exception():
+ return get_exception() != null
+
+func was_cancelled():
+ return is_exception() and get_exception().cancelled
+
+func get_exception() -> NakamaException:
+ return _ex as NakamaException
+
+func _to_string():
+ if is_exception():
+ return get_exception()._to_string()
+ return "NakamaAsyncResult<>"
+
+static func _safe_ret(p_obj, p_type : GDScript):
+ if is_instance_of(p_obj, p_type):
+ return p_obj # Correct type
+ elif p_obj is NakamaException:
+ return p_type.new(p_obj) # It's an exception. Incapsulate it
+ return p_type.new(NakamaException.new()) # It's something else. generate an exception
diff --git a/game/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd.uid b/game/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd.uid
new file mode 100644
index 0000000..1b82c7e
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd.uid
@@ -0,0 +1 @@
+uid://tu8l6uht0rb7
diff --git a/game/addons/com.heroiclabs.nakama/utils/NakamaException.gd b/game/addons/com.heroiclabs.nakama/utils/NakamaException.gd
new file mode 100644
index 0000000..f85ae09
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/utils/NakamaException.gd
@@ -0,0 +1,42 @@
+extends RefCounted
+
+# An exception generated during a request.
+# Usually contains at least an error message.
+class_name NakamaException
+
+var _status_code : int = -1
+var status_code : int:
+ set(v):
+ pass
+ get:
+ return _status_code
+
+var _grpc_status_code : int = -1
+var grpc_status_code : int:
+ set(v):
+ pass
+ get:
+ return _grpc_status_code
+
+var _message : String = ""
+var message : String:
+ set(v):
+ pass
+ get:
+ return _message
+
+var _cancelled : bool = false
+var cancelled : bool:
+ set(v):
+ pass
+ get:
+ return _cancelled
+
+func _init(p_message : String = "", p_status_code : int = -1, p_grpc_status_code : int = -1, p_cancelled : bool = false):
+ _status_code = p_status_code
+ _grpc_status_code = p_grpc_status_code
+ _message = p_message
+ _cancelled = p_cancelled
+
+func _to_string() -> String:
+ return "NakamaException(StatusCode={%s}, Message='{%s}', GrpcStatusCode={%s})" % [_status_code, _message, _grpc_status_code]
diff --git a/game/addons/com.heroiclabs.nakama/utils/NakamaException.gd.uid b/game/addons/com.heroiclabs.nakama/utils/NakamaException.gd.uid
new file mode 100644
index 0000000..1345de7
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/utils/NakamaException.gd.uid
@@ -0,0 +1 @@
+uid://bvqg0eg8b6d2w
diff --git a/game/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd b/game/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd
new file mode 100644
index 0000000..4f01830
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd
@@ -0,0 +1,38 @@
+extends RefCounted
+class_name NakamaLogger
+
+enum LOG_LEVEL {NONE, ERROR, WARNING, INFO, VERBOSE, DEBUG}
+
+var _level = LOG_LEVEL.ERROR
+var _module = "Nakama"
+
+func _init(p_module : String = "Nakama", p_level : int = LOG_LEVEL.ERROR):
+ _level = p_level
+ _module = p_module
+
+func _log(level : int, msg):
+ if level <= _level:
+ if level == LOG_LEVEL.ERROR:
+ printerr("=== %s : ERROR === %s" % [_module, str(msg)])
+ else:
+ var what = "=== UNKNOWN === "
+ for k in LOG_LEVEL:
+ if level == LOG_LEVEL[k]:
+ what = "=== %s : %s === " % [_module, k]
+ break
+ print(what + str(msg))
+
+func error(msg):
+ _log(LOG_LEVEL.ERROR, msg)
+
+func warning(msg):
+ _log(LOG_LEVEL.WARNING, msg)
+
+func info(msg):
+ _log(LOG_LEVEL.INFO, msg)
+
+func verbose(msg):
+ _log(LOG_LEVEL.VERBOSE, msg)
+
+func debug(msg):
+ _log(LOG_LEVEL.DEBUG, msg)
diff --git a/game/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd.uid b/game/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd.uid
new file mode 100644
index 0000000..8c372f5
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd.uid
@@ -0,0 +1 @@
+uid://mitcyrhr25eu
diff --git a/game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd b/game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd
new file mode 100644
index 0000000..66c15bb
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd
@@ -0,0 +1,363 @@
+extends RefCounted
+class_name NakamaMultiplayerBridge
+
+enum MatchState {
+ DISCONNECTED,
+ JOINING,
+ CONNECTED,
+ SOCKET_CLOSED,
+}
+
+enum MetaMessageType {
+ CLAIM_HOST,
+ ASSIGN_PEER_ID,
+}
+
+# Read-only variables.
+var _nakama_socket: NakamaSocket
+var nakama_socket: NakamaSocket:
+ get: return _nakama_socket
+ set(_v): pass
+var _match_state: int = MatchState.DISCONNECTED
+var match_state: int:
+ get: return _match_state
+ set(_v): pass
+var _match_id := ''
+var match_id: String:
+ get: return _match_id
+ set(_v): pass
+var _multiplayer_peer: NakamaMultiplayerPeer = NakamaMultiplayerPeer.new()
+var multiplayer_peer: NakamaMultiplayerPeer:
+ get: return _multiplayer_peer
+ set(_v): pass
+
+# Configuration that can be set by the developer.
+var meta_op_code: int = 9001
+var rpc_op_code: int = 9002
+
+# Internal variables.
+var _my_session_id: String
+var _my_peer_id: int = 0
+var _id_map := {}
+var _users := {}
+var _matchmaker_ticket := ''
+
+class User extends RefCounted:
+ var presence
+ var peer_id: int = 0
+
+ func _init(p_presence) -> void:
+ presence = p_presence
+
+signal match_join_error (exception)
+signal match_joined ()
+
+func _set_readonly(_value) -> void:
+ pass
+
+func _init(p_nakama_socket: NakamaSocket) -> void:
+ _nakama_socket = p_nakama_socket
+ _nakama_socket.received_match_presence.connect(self._on_nakama_socket_received_match_presence)
+ _nakama_socket.received_matchmaker_matched.connect(self._on_nakama_socket_received_matchmaker_matched)
+ _nakama_socket.received_match_state.connect(self._on_nakama_socket_received_match_state)
+ _nakama_socket.closed.connect(self._on_nakama_socket_closed)
+
+ _multiplayer_peer.packet_generated.connect(self._on_multiplayer_peer_packet_generated)
+ _multiplayer_peer.set_connection_status(MultiplayerPeer.CONNECTION_CONNECTING)
+
+func create_match() -> void:
+ if _match_state != MatchState.DISCONNECTED:
+ push_error("Cannot create match when state is %s" % MatchState.keys()[_match_state])
+ return
+
+ _match_state = MatchState.JOINING
+ multiplayer_peer.set_connection_status(MultiplayerPeer.CONNECTION_CONNECTING)
+
+ var res = await _nakama_socket.create_match_async()
+ if res.is_exception():
+ match_join_error.emit(res.get_exception())
+ leave()
+ return
+
+ _setup_match(res)
+ _setup_host()
+
+func join_match(p_match_id: String) -> void:
+ if _match_state != MatchState.DISCONNECTED:
+ push_error("Cannot join match when state is %s" % MatchState.keys()[_match_state])
+ return
+
+ _match_state = MatchState.JOINING
+ multiplayer_peer.set_connection_status(MultiplayerPeer.CONNECTION_CONNECTING)
+
+ var res = await _nakama_socket.join_match_async(p_match_id)
+ if res.is_exception():
+ match_join_error.emit(res.get_exception())
+ leave()
+ return
+
+ _setup_match(res)
+
+func join_named_match(_match_name: String) -> void:
+ if _match_state != MatchState.DISCONNECTED:
+ push_error("Cannot join match when state is %s" % MatchState.keys()[_match_state])
+ return
+
+ _match_state = MatchState.JOINING
+ multiplayer_peer.set_connection_status(MultiplayerPeer.CONNECTION_CONNECTING)
+
+ var res = await _nakama_socket.create_match_async(_match_name)
+ if res.is_exception():
+ match_join_error.emit(res.get_exception())
+ leave()
+ return
+
+ _setup_match(res)
+ if res.size == 0 or (res.size == 1 and res.presences.size() == 0):
+ _setup_host()
+
+func start_matchmaking(ticket) -> void:
+ if _match_state != MatchState.DISCONNECTED:
+ push_error("Cannot start matchmaking when state is %s" % MatchState.keys()[_match_state])
+ return
+ if ticket.is_exception():
+ push_error("Ticket with exception passed into start_matchmaking()")
+ return
+
+ _match_state = MatchState.JOINING
+ multiplayer_peer.set_connection_status(MultiplayerPeer.CONNECTION_CONNECTING)
+
+ _matchmaker_ticket = ticket.ticket
+
+func _on_nakama_socket_received_matchmaker_matched(matchmaker_matched) -> void:
+ if _matchmaker_ticket != matchmaker_matched.ticket:
+ return
+
+ # Get a list of sorted session ids.
+ var session_ids := []
+ for matchmaker_user in matchmaker_matched.users:
+ session_ids.append(matchmaker_user.presence.session_id)
+ session_ids.sort()
+
+ var res = await _nakama_socket.join_matched_async(matchmaker_matched)
+ if res.is_exception():
+ match_join_error.emit(res.get_exception())
+ leave()
+ return
+
+ _setup_match(res)
+
+ # If our session is the first alphabetically, then we'll be the host.
+ if _my_session_id == session_ids[0]:
+ _setup_host()
+
+ # Add all of the existing peers.
+ for presence in res.presences:
+ if presence.session_id != _my_session_id:
+ _host_add_peer(presence)
+
+func _on_nakama_socket_closed() -> void:
+ match_state = MatchState.SOCKET_CLOSED
+ _cleanup()
+
+func get_user_presence_for_peer(peer_id: int) -> NakamaRTAPI.UserPresence:
+ var session_id = _id_map.get(peer_id)
+ if session_id == null:
+ return null
+ var user = _users.get(session_id)
+ if user == null:
+ return null
+ return user.presence
+
+func leave() -> void:
+ if _match_state == MatchState.DISCONNECTED:
+ return
+ _match_state = MatchState.DISCONNECTED
+
+ if _match_id:
+ await _nakama_socket.leave_match_async(_match_id)
+ if _matchmaker_ticket:
+ await _nakama_socket.remove_matchmaker_async(_matchmaker_ticket)
+
+ _cleanup()
+
+func _cleanup() -> void:
+ for peer_id in _id_map:
+ multiplayer_peer.peer_disconnected.emit(peer_id)
+
+ _match_id = ''
+ _matchmaker_ticket = ''
+ _my_session_id = ''
+ _my_peer_id = 0
+ _id_map.clear()
+ _users.clear()
+
+ _multiplayer_peer.set_connection_status(MultiplayerPeer.CONNECTION_DISCONNECTED)
+
+func _setup_match(res) -> void:
+ _match_id = res.match_id
+ _my_session_id = res.self_user.session_id
+
+ _users[_my_session_id] = User.new(res.self_user)
+
+ for presence in res.presences:
+ if not _users.has(presence.session_id):
+ _users[presence.session_id] = User.new(presence)
+
+func _setup_host() -> void:
+ # Claim id 1 and start the match.
+ _my_peer_id = 1
+ _map_id_to_session(1, _my_session_id)
+ _match_state = MatchState.CONNECTED
+ _multiplayer_peer.initialize(_my_peer_id)
+ match_joined.emit()
+
+func _generate_id(session_id: String) -> int:
+ # Peer ids can only be positive 32-bit signed integers.
+ var peer_id: int = session_id.hash() & 0x7FFFFFFF
+
+ # If this peer id is already taken, try to find another.
+ while peer_id <= 1 or _id_map.has(peer_id):
+ peer_id += 1
+ if peer_id > 0x7FFFFFFF or peer_id <= 0:
+ peer_id = randi() & 0x7FFFFFFF
+
+ return peer_id
+
+func _map_id_to_session(peer_id: int, session_id: String) -> void:
+ _id_map[peer_id] = session_id
+ _users[session_id].peer_id = peer_id
+
+func _host_add_peer(presence) -> void:
+ var peer_id = _generate_id(presence.session_id)
+ _map_id_to_session(peer_id, presence.session_id)
+
+ # Tell them we are the host.
+ _nakama_socket.send_match_state_async(_match_id, meta_op_code, JSON.stringify({
+ type = MetaMessageType.CLAIM_HOST,
+ }), [presence])
+
+ # Tell them about all the other connected peers.
+ for other_peer_id in _id_map:
+ var other_session_id = _id_map[other_peer_id]
+ if other_session_id == presence.session_id or other_session_id == _my_session_id:
+ continue
+ _nakama_socket.send_match_state_async(_match_id, meta_op_code, JSON.stringify({
+ type = MetaMessageType.ASSIGN_PEER_ID,
+ session_id = other_session_id,
+ peer_id = other_peer_id,
+ }), [presence])
+
+ # Assign them a peer_id (tell everyone about it).
+ _nakama_socket.send_match_state_async(_match_id, meta_op_code, JSON.stringify({
+ type = MetaMessageType.ASSIGN_PEER_ID,
+ session_id = presence.session_id,
+ peer_id = peer_id,
+ }))
+
+ _multiplayer_peer.peer_connected.emit(peer_id)
+
+func _on_nakama_socket_received_match_presence(event) -> void:
+ if _match_state == MatchState.DISCONNECTED:
+ return
+ if event.match_id != _match_id:
+ return
+
+ for presence in event.joins:
+ if not _users.has(presence.session_id):
+ _users[presence.session_id] = User.new(presence)
+
+ # If we are the host, and they don't yet have a peer id, then let's
+ # generate a new id for them and send all the necessary messages.
+ if _my_peer_id == 1 and _users[presence.session_id].peer_id == 0:
+ _host_add_peer(presence)
+
+ for presence in event.leaves:
+ if not _users.has(presence.session_id):
+ continue
+
+ var peer_id = _users[presence.session_id].peer_id
+
+ _multiplayer_peer.peer_disconnected.emit(peer_id)
+
+ _users.erase(presence.session_id)
+ _id_map.erase(peer_id)
+
+func _parse_json(data: String):
+ var json = JSON.new()
+ if json.parse(data) != OK:
+ return null
+ var content = json.get_data()
+ if not content is Dictionary:
+ return null
+ return content
+
+func _on_nakama_socket_received_match_state(data) -> void:
+ if _match_state == MatchState.DISCONNECTED:
+ return
+ if data.match_id != _match_id:
+ return
+
+ if data.op_code == meta_op_code:
+ var content = _parse_json(data.data)
+ if content == null:
+ return
+ var type = content['type']
+ #print ("RECEIVED: ", content)
+
+ if type == MetaMessageType.CLAIM_HOST:
+ if _id_map.has(1):
+ # @todo Can we mediate this dispute?
+ push_error("User %s claiming to be host, when user %s has already claimed it" % [data.presence.session_id, _id_map[1]])
+ else:
+ _map_id_to_session(1, data.presence.session_id)
+ return
+
+ # Ensure that any meta messages are coming from the host!
+ if data.presence.session_id != _id_map[1]:
+ push_error("Received meta message from user %s who isn't the host: %s" % [data.presence.session_id, content])
+ return
+
+ if type == MetaMessageType.ASSIGN_PEER_ID:
+ var session_id = content['session_id']
+ var peer_id = content['peer_id']
+
+ if _users.has(session_id) and _users[session_id].peer_id != 0:
+ push_error("Attempting to assign peer id %s to %s which already has id %s" % [
+ peer_id,
+ session_id,
+ _users[session_id].peer_id,
+ ])
+ return
+
+ _map_id_to_session(peer_id, session_id)
+
+ if _my_session_id == session_id:
+ _match_state = MatchState.CONNECTED
+ _multiplayer_peer.initialize(peer_id)
+ _multiplayer_peer.set_connection_status(MultiplayerPeer.CONNECTION_CONNECTED)
+ match_joined.emit()
+ _multiplayer_peer.peer_connected.emit(1)
+ else:
+ _multiplayer_peer.peer_connected.emit(peer_id)
+ else:
+ _nakama_socket.logger.error("Received meta message with unknown type: %s" % type)
+ elif data.op_code == rpc_op_code:
+ var from_session_id: String = data.presence.session_id
+ if not _users.has(from_session_id) or _users[from_session_id].peer_id == 0:
+ push_error("Received RPC from %s which isn't assigned a peer id" % data.presence.session_id)
+ return
+ var from_peer_id = _users[from_session_id].peer_id
+ _multiplayer_peer.deliver_packet(data.binary_data, from_peer_id)
+
+func _on_multiplayer_peer_packet_generated(peer_id: int, buffer: PackedByteArray) -> void:
+ if match_state == MatchState.CONNECTED:
+ var target_presences = null
+ if peer_id > 0:
+ if not _id_map.has(peer_id):
+ push_error("Attempting to send RPC to unknown peer id: %s" % peer_id)
+ return
+ target_presences = [ _users[_id_map[peer_id]].presence ]
+ _nakama_socket.send_match_state_raw_async(_match_id, rpc_op_code, buffer, target_presences)
+ else:
+ push_error("RPC sent while the NakamaMultiplayerBridge isn't connected!")
diff --git a/game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd.uid b/game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd.uid
new file mode 100644
index 0000000..a6edda6
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd.uid
@@ -0,0 +1 @@
+uid://b7fw6exsrtog7
diff --git a/game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd b/game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd
new file mode 100644
index 0000000..6bfb8d9
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd
@@ -0,0 +1,97 @@
+extends MultiplayerPeerExtension
+class_name NakamaMultiplayerPeer
+
+const MAX_PACKET_SIZE := 1 << 24
+
+var _self_id := 0
+var _connection_status: ConnectionStatus = CONNECTION_DISCONNECTED
+var _refusing_new_connections := false
+var _target_id := 0
+
+class Packet extends RefCounted:
+ var data: PackedByteArray
+ var from: int
+
+ func _init(p_data: PackedByteArray, p_from: int) -> void:
+ data = p_data
+ from = p_from
+
+var _incoming_packets := []
+
+signal packet_generated (peer_id, buffer)
+
+func _get_packet_script() -> PackedByteArray:
+ if _incoming_packets.size() == 0:
+ return PackedByteArray()
+ return _incoming_packets.pop_front().data
+
+func _get_packet_mode() -> int:
+ return TRANSFER_MODE_RELIABLE
+
+func _get_packet_channel() -> int:
+ return 0
+
+func _put_packet_script(p_buffer: PackedByteArray) -> Error:
+ packet_generated.emit(_target_id, p_buffer)
+ return OK
+
+func _get_available_packet_count() -> int:
+ return _incoming_packets.size()
+
+func _get_max_packet_size() -> int:
+ return MAX_PACKET_SIZE
+
+func _set_transfer_channel(p_channel) -> void:
+ pass
+
+func _get_transfer_channel() -> int:
+ return 0
+
+func _set_transfer_mode(p_mode: TransferMode) -> void:
+ pass
+
+func _get_transfer_mode() -> TransferMode:
+ return TRANSFER_MODE_RELIABLE
+
+func _set_target_peer(p_peer_id: int) -> void:
+ _target_id = p_peer_id
+
+func _get_packet_peer() -> int:
+ if _connection_status != CONNECTION_CONNECTED:
+ return 1
+ if _incoming_packets.size() == 0:
+ return 1
+
+ return _incoming_packets[0].from
+
+func _is_server() -> bool:
+ return _self_id == 1
+
+func _poll() -> void:
+ pass
+
+func _get_unique_id() -> int:
+ return _self_id
+
+func _set_refuse_new_connections(p_enable: bool) -> void:
+ _refusing_new_connections = p_enable
+
+func _is_refusing_new_connections() -> bool:
+ return _refusing_new_connections
+
+func _get_connection_status() -> ConnectionStatus:
+ return _connection_status
+
+func initialize(p_self_id: int) -> void:
+ if _connection_status != CONNECTION_CONNECTING:
+ return
+ _self_id = p_self_id
+ if _self_id == 1:
+ _connection_status = CONNECTION_CONNECTED
+
+func set_connection_status(p_connection_status: int) -> void:
+ _connection_status = p_connection_status
+
+func deliver_packet(p_data: PackedByteArray, p_from_peer_id: int) -> void:
+ var packet = Packet.new(p_data, p_from_peer_id);
+ _incoming_packets.push_back(packet)
diff --git a/game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd.uid b/game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd.uid
new file mode 100644
index 0000000..5e00b97
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd.uid
@@ -0,0 +1 @@
+uid://d1eb1qsvv1pnp
diff --git a/game/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd b/game/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd
new file mode 100644
index 0000000..cc8f284
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd
@@ -0,0 +1,160 @@
+extends RefCounted
+class_name NakamaSerializer
+
+static func serialize(p_obj : Object) -> Dictionary:
+ var out = {}
+ var schema = p_obj.get("_SCHEMA")
+ if schema == null:
+ return {} # No schema defined
+ for k in schema:
+ var prop = schema[k]
+ var val = p_obj.get(prop["name"])
+ if val == null:
+ continue
+ var type = prop["type"]
+ var content = prop.get("content", TYPE_NIL)
+ if typeof(content) == TYPE_STRING:
+ content = TYPE_OBJECT
+ var val_type = typeof(val)
+ match val_type:
+ TYPE_OBJECT: # Simple objects
+ out[k] = serialize(val)
+ TYPE_ARRAY: # Array of objects
+ var arr = []
+ for e in val:
+ if typeof(e) != TYPE_OBJECT:
+ continue
+ arr.append(serialize(e))
+ out[k] = arr
+ TYPE_PACKED_INT32_ARRAY, TYPE_PACKED_STRING_ARRAY: # Array of ints, bools, or strings
+ var arr = []
+ for e in val:
+ if content == TYPE_BOOL:
+ e = bool(e)
+ if typeof(e) != content:
+ continue
+ arr.append(e)
+ out[k] = arr
+ TYPE_DICTIONARY: # Maps
+ var dict = {}
+ if content == TYPE_OBJECT: # Map of objects
+ for l in val:
+ if typeof(val[l]) != TYPE_OBJECT:
+ continue
+ dict[l] = serialize(val[l])
+ else: # Map of simple types
+ for l in val:
+ var e = val[l]
+ if content == TYPE_FLOAT:
+ e = float(e)
+ elif content == TYPE_INT:
+ e = int(e)
+ elif content == TYPE_BOOL:
+ e = bool(e)
+ if typeof(e) != content:
+ continue
+ dict[l] = e
+ out[k] = dict
+ _:
+ out[k] = val
+ return out
+
+static func deserialize(p_ns : GDScript, p_cls_name : String, p_dict : Dictionary) -> Object:
+ var cls : GDScript = p_ns.get(p_cls_name)
+ var schema = cls.get("_SCHEMA")
+ if schema == null:
+ return NakamaException.new() # No schema defined
+ var obj = cls.new()
+ for k in schema:
+ var prop = schema[k]
+ var pname = prop["name"]
+ var type = prop["type"]
+ var required = prop["required"]
+ var content = prop.get("content", TYPE_NIL)
+ var type_cmp = type
+ if typeof(type) == TYPE_STRING: # A class
+ type_cmp = TYPE_DICTIONARY
+ if type_cmp == TYPE_PACKED_STRING_ARRAY or type_cmp == TYPE_PACKED_INT32_ARRAY: # A specialized array
+ type_cmp = TYPE_ARRAY
+
+ var content_cmp = content
+ if typeof(content) == TYPE_STRING: # A dictionary or array of classes
+ content_cmp = TYPE_DICTIONARY
+
+ var val = p_dict.get(k, null)
+
+ # Ints might and up being recognized as floats. Change that if needed
+ if type_cmp == TYPE_INT:
+ if typeof(val) == TYPE_FLOAT:
+ val = int(val)
+ elif typeof(val) == TYPE_STRING and val.is_valid_int():
+ val = val.to_int()
+
+ if typeof(val) == type_cmp:
+ if typeof(type) == TYPE_STRING:
+ obj.set(pname, deserialize(p_ns, type, val))
+ elif type_cmp == TYPE_DICTIONARY:
+ var v = {}
+ for l in val:
+ if typeof(content) == TYPE_STRING:
+ v[l] = deserialize(p_ns, content, val[l])
+ elif content == TYPE_FLOAT:
+ v[l] = float(val[l])
+ elif content == TYPE_INT:
+ v[l] = int(val[l])
+ elif content == TYPE_BOOL:
+ v[l] = bool(val[l])
+ else:
+ v[l] = str(val[l])
+ obj.set(pname, v)
+ elif type_cmp == TYPE_ARRAY:
+ var v
+ match content:
+ TYPE_INT, TYPE_BOOL: v = PackedInt32Array()
+ TYPE_STRING: v = PackedStringArray()
+ _: v = Array()
+ for e in val:
+ if typeof(content) == TYPE_STRING:
+ v.append(deserialize(p_ns, content, e))
+ elif content == TYPE_FLOAT:
+ v.append(float(e))
+ elif content == TYPE_INT:
+ v.append(int(e))
+ elif content == TYPE_BOOL:
+ v.append(bool(e))
+ else:
+ v.append(str(e))
+ obj.set(pname, v)
+ else:
+ obj.set(pname, val)
+ elif required:
+ obj._ex = NakamaException.new("ERROR [%s]: Missing or invalid required prop %s = %s:\n\t%s" % [p_cls_name, prop, p_dict.get(k), p_dict])
+ return obj
+ return obj
+
+
+###
+# Compatibility with Godot 3.1 which does not expose String.http_escape
+###
+const HEX = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"]
+
+static func escape_http(p_str : String) -> String:
+ var out : String = ""
+ for o in p_str:
+ if (o == '.' or o == '-' or o == '_' or o == '~' or
+ (o >= 'a' and o <= 'z') or
+ (o >= 'A' and o <= 'Z') or
+ (o >= '0' and o <= '9')):
+ out += o
+ else:
+ for b in o.to_utf8_buffer():
+ out += "%%%s" % to_hex(b)
+ return out
+
+static func to_hex(p_val : int) -> String:
+ var v := p_val
+ var o := ""
+ while v != 0:
+ o = HEX[v % 16] + o
+ v /= 16
+ return o
diff --git a/game/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd.uid b/game/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd.uid
new file mode 100644
index 0000000..0367efc
--- /dev/null
+++ b/game/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd.uid
@@ -0,0 +1 @@
+uid://bgwx1pqo2dfq0
diff --git a/game/codegen/README.md b/game/codegen/README.md
new file mode 100644
index 0000000..ee85ea3
--- /dev/null
+++ b/game/codegen/README.md
@@ -0,0 +1,21 @@
+codegen
+=======
+
+> A util tool to generate a client from the Swagger spec of Nakama's server API.
+
+## Usage
+
+```shell
+go run main.go "$GOPATH/src/github.com/heroiclabs/nakama/apigrpc/apigrpc.swagger.json" > ../addons/com.heroiclabs.nakama/api/NakamaAPI.gd
+```
+
+### Rationale
+
+We want to maintain a simple lean low level client within our GDScript client which has minimal dependencies so we built our own. This gives us complete control over the dependencies required and structure of the code generated.
+
+The generated code is designed to be supported Godot Engine `3.1+`.
+
+### Limitations
+
+The code generator has __only__ been checked against the Swagger specification generated for Nakama server. YMMV.
+
diff --git a/game/codegen/main.go b/game/codegen/main.go
new file mode 100644
index 0000000..4eff791
--- /dev/null
+++ b/game/codegen/main.go
@@ -0,0 +1,639 @@
+// Copyright 2018 The Nakama Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+ "bufio"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+ "text/template"
+)
+
+var utilities = map[string]string {
+ "ApiAccount":
+`
+
+ var _wallet_dict = null
+ var wallet_dict : Dictionary:
+ get:
+ if _wallet_dict == null:
+ if _wallet == null:
+ return {}
+ var json = JSON.new()
+ if json.parse(_wallet) != OK:
+ return {}
+ _wallet_dict = json.get_data()
+ return _wallet_dict as Dictionary
+`,
+}
+
+const codeTemplate string = `### Code generated by codegen/main.go. DO NOT EDIT. ###
+
+extends RefCounted
+class_name NakamaAPI
+{{- range $defname, $definition := .Definitions }}
+{{- $classname := $defname | title }}
+{{- if isRefToEnum $classname }}
+
+# {{ enumSummary $definition | stripNewlines }}
+{{- range $idx, $val := ($definition | enumDescriptions) }}
+# {{ $val }}
+{{- end -}}
+# {{ $definition | enumDescriptions }}
+enum {{ $classname | title }} { {{- range $idx, $enum := $definition.Enum }}{{ $enum }} = {{ $idx }},{{- end -}} }
+{{- else }}
+
+# {{ $definition.Description | stripNewlines }}
+class {{ $classname }} extends NakamaAsyncResult:
+
+ const _SCHEMA = {
+ {{- range $propname, $property := $definition.Properties }}
+ {{- $fieldname := $propname | pascalToSnake }}
+ {{- $_field := printf "_%s" $fieldname }}
+ {{- $gdType := godotType $property.Type $property.Ref $property.Items.Type $property.Items.Ref (isRefToEnum (cleanRef $property.Ref)) }}
+ "{{ $fieldname }}": {"name": "{{ $_field }}", "type": {{ $gdType | godotSchemaType }}, "required": false
+ {{- if eq $property.Type "array" -}}
+ , "content": {{ (godotType $property.Items.Type $property.Items.Ref "" "" false) | godotSchemaType }}
+ {{- else if eq $property.Type "object" -}}
+ , "content": {{ (godotType $property.AdditionalProperties.Type "" "" "" false) | godotSchemaType }}
+ {{- end -}}
+ },
+ {{- end }}
+ }
+
+ {{- range $propname, $property := $definition.Properties }}
+ {{- $fieldname := $propname | pascalToSnake }}
+ {{- $_field := printf "_%s" $fieldname }}
+ {{- $gdType := godotType $property.Type $property.Ref $property.Items.Type $property.Items.Ref (isRefToEnum (cleanRef $property.Ref)) }}
+ {{- $gdDef := $gdType | godotDef }}
+
+ # {{ $property.Description }}
+ var {{ $_field }}
+ var {{ $fieldname }} : {{ $gdType }}:
+ get:
+ {{- if $property.Ref }}
+ {{- if isRefToEnum (cleanRef $property.Ref) }}{{/* Enums */}}
+ return {{ cleanRef $property.Ref }}.values()[0] if not {{ cleanRef $property.Ref }}.values().has({{ $_field }}) else {{ $_field }}
+ {{- else }}{{/* Object reference */}}
+ return _{{ $fieldname }} as {{ $gdType }}
+ {{- end }}
+ {{- else if eq $property.Type "object"}}{{/* Dictionaries */}}
+ return Dictionary() if not {{ $_field }} is Dictionary else {{ $_field }}.duplicate()
+ {{- else }}{{/* Simple type */}}
+ return {{ $gdDef }} if not {{ $_field }} is {{ $gdType }} else {{ $gdType }}({{ $_field }})
+ {{- end }}
+ {{- end }}
+
+ {{- godotClassUtils $classname }}
+
+ func _init(p_exception = null):
+ super(p_exception)
+
+ static func create(p_ns : GDScript, p_dict : Dictionary) -> {{ $classname }}:
+ return _safe_ret(NakamaSerializer.deserialize(p_ns, "{{ $classname }}", p_dict), {{ $classname }}) as {{ $classname }}
+
+ func serialize() -> Dictionary:
+ return NakamaSerializer.serialize(self)
+
+ func _to_string() -> String:
+ if is_exception():
+ return get_exception()._to_string()
+ var output : String = ""
+ {{- range $propname, $property := $definition.Properties }}
+ {{- $fieldname := $propname | pascalToSnake }}
+ {{- $_field := printf "_%s" $fieldname }}
+ {{- if eq $property.Type "array" }}
+ output += "{{ $fieldname }}: %s, " % [{{ $_field }}]
+ {{- else if eq $property.Type "object" }}
+ var map_string : String = ""
+ if typeof({{ $_field }}) == TYPE_DICTIONARY:
+ for k in {{ $_field }}:
+ map_string += "{%s=%s}, " % [k, {{ $_field }}[k]]
+ output += "{{ $fieldname }}: [%s], " % map_string
+ {{- else }}
+ output += "{{ $fieldname }}: %s, " % {{ $_field }}
+ {{- end }}
+ {{- end }}
+ return output
+ {{- end }}
+{{- end }}
+
+# The low level client for the Nakama API.
+class ApiClient extends RefCounted:
+
+ var _base_uri : String
+
+ var _http_adapter
+ var _namespace : GDScript
+ var _server_key : String
+ var auto_refresh := true
+ var auto_refresh_time := 300
+
+ var auto_retry : bool:
+ set(p_value):
+ _http_adapter.auto_retry = p_value
+ get:
+ return _http_adapter.auto_retry
+
+ var auto_retry_count : int:
+ set(p_value):
+ _http_adapter.auto_retry_count = p_value
+ get:
+ return _http_adapter.auto_retry_count
+
+ var auto_retry_backoff_base : int:
+ set(p_value):
+ _http_adapter.auto_retry_backoff_base = p_value
+ get:
+ return _http_adapter.auto_retry_backoff_base
+
+ var last_cancel_token:
+ get:
+ return _http_adapter.get_last_token()
+
+ func _init(p_base_uri : String, p_http_adapter, p_namespace : GDScript, p_server_key : String, p_timeout : int = 10):
+ _base_uri = p_base_uri
+ _http_adapter = p_http_adapter
+ _http_adapter.timeout = p_timeout
+ _namespace = p_namespace
+ _server_key = p_server_key
+
+ func _refresh_session(p_session : NakamaSession):
+ if auto_refresh and p_session.is_valid() and p_session.refresh_token and not p_session.is_refresh_expired() and p_session.would_expire_in(auto_refresh_time):
+ var request = ApiSessionRefreshRequest.new()
+ request._token = p_session.refresh_token
+ return await session_refresh_async(_server_key, "", request)
+ return null
+
+ func cancel_request(p_token):
+ if p_token:
+ _http_adapter.cancel_request(p_token)
+
+ {{- range $url, $path := .Paths }}
+ {{- range $method, $operation := $path}}
+
+ # {{ $operation.Summary | stripNewlines }}
+ {{- if $operation.Responses.Ok.Schema.Ref }}
+ func {{ $operation.OperationId | apiFuncName }}_async(
+ {{- else }}
+ func {{ $operation.OperationId | apiFuncName }}_async(
+ {{- end}}
+
+ {{- if $operation.Security }}
+ {{- with (index $operation.Security 0) }}
+ {{- range $key, $value := . }}
+ {{- if eq $key "BasicAuth" }}
+ p_basic_auth_username : String
+ , p_basic_auth_password : String
+ {{- else if eq $key "HttpKeyAuth" }}
+ p_bearer_token : String
+ {{- end }}
+ {{- end }}
+ {{- end }}
+ {{- else }}
+ p_session : NakamaSession
+ {{- end }}
+
+ {{- range $parameter := $operation.Parameters }}
+ {{- $argument := $parameter.Name | prependParameter }}
+ {{- if not $parameter.Required }}{{/* Godot does not support typed optional parameters yet. */}}
+ , {{ $argument }} = null # : {{ $parameter.Type }}
+ {{- else if eq $parameter.In "body" }}
+ {{- if eq $parameter.Schema.Type "string" }}
+ , {{ $argument }} : String
+ {{- else }}
+ , {{ $argument }} : {{ $parameter.Schema.Ref | cleanRef }}
+ {{- end }}
+ {{- else }}
+ , {{ $argument }} : {{ godotType $parameter.Type $parameter.Schema.Ref $parameter.Items.Type "" (isRefToEnum (cleanRef $parameter.Schema.Ref)) }}
+ {{- end }}
+ {{- end }}
+ )
+ {{- if $operation.Responses.Ok.Schema.Ref }} -> {{ $operation.Responses.Ok.Schema.Ref | cleanRef }}
+ {{- else }} -> NakamaAsyncResult
+ {{- end }}:
+ {{- $classname := "NakamaAsyncResult" }}
+ {{- if $operation.Responses.Ok.Schema.Ref }}
+ {{- $classname = $operation.Responses.Ok.Schema.Ref | cleanRef }}
+ {{- end }}
+ {{- if not $operation.Security }}
+ var try_refresh = await _refresh_session(p_session)
+ if try_refresh != null:
+ if try_refresh.is_exception():
+ return {{ $classname }}.new(try_refresh.get_exception())
+ await p_session.refresh(try_refresh)
+ {{- end }}
+ var urlpath : String = "{{- $url }}"
+ {{- range $parameter := $operation.Parameters }}
+ {{- $argument := $parameter.Name | prependParameter }}
+ {{- if eq $parameter.In "path" }}
+ urlpath = urlpath.replace("{{- print "{" $parameter.Name "}"}}", NakamaSerializer.escape_http({{ $argument }}))
+ {{- end }}
+ {{- end }}
+ var query_params = ""
+ {{- range $parameter := $operation.Parameters }}
+ {{- $argument := $parameter.Name | prependParameter }}
+ {{- $snakecase := $parameter.Name | pascalToSnake }}
+ {{- if eq $parameter.In "query"}}
+ {{- if $parameter.Required }}
+ if true: # Hack for static checks
+ {{- else }}
+ if {{ $argument }} != null:
+ {{- end }}
+ {{- if eq $parameter.Type "integer" }}
+ query_params += "{{- $snakecase }}=%d&" % {{ $argument }}
+ {{- else if eq $parameter.Type "string" }}
+ query_params += "{{- $snakecase }}=%s&" % NakamaSerializer.escape_http({{ $argument }})
+ {{- else if eq $parameter.Type "boolean" }}
+ query_params += "{{- $snakecase }}=%s&" % str(bool({{ $argument }})).to_lower()
+ {{- else if eq $parameter.Type "array" }}
+ for elem in {{ $argument }}:
+ query_params += "{{- $snakecase }}=%s&" % elem
+ {{- else }}
+ {{ $parameter }} // ERROR
+ {{- end }}
+ {{- end }}
+ {{- end }}
+ var uri = "%s%s%s" % [_base_uri, urlpath, "?" + query_params if query_params else ""]
+ var method = "{{- $method | uppercase }}"
+ var headers = {}
+ {{- if $operation.Security }}
+ {{- with (index $operation.Security 0) }}
+ {{- range $key, $value := . }}
+ {{- if eq $key "BasicAuth" }}
+ var credentials = Marshalls.utf8_to_base64(p_basic_auth_username + ":" + p_basic_auth_password)
+ var header = "Basic %s" % credentials
+ headers["Authorization"] = header
+ {{- else if eq $key "HttpKeyAuth" }}
+ if (p_bearer_token):
+ var header = "Bearer %s" % p_bearer_token
+ headers["Authorization"] = header
+ {{- end }}
+ {{- end }}
+ {{- end }}
+ {{- else }}
+ var header = "Bearer %s" % p_session.token
+ headers["Authorization"] = header
+ {{- end }}
+
+ var content : PackedByteArray
+ {{- range $parameter := $operation.Parameters }}
+ {{- $argument := $parameter.Name | prependParameter }}
+ {{- if eq $parameter.In "body" }}
+ {{- if eq $parameter.Schema.Type "string" }}
+ content = JSON.stringify({{ $argument }}).to_utf8_buffer()
+ {{- else }}
+ content = JSON.stringify({{ $argument }}.serialize()).to_utf8_buffer()
+ {{- end }}
+ {{- end }}
+ {{- end }}
+
+ var result = await _http_adapter.send_async(method, uri, headers, content)
+ if result is NakamaException:
+ return {{ $classname }}.new(result)
+
+ {{- if $operation.Responses.Ok.Schema.Ref }}
+ var out : {{ $classname }} = NakamaSerializer.deserialize(_namespace, "{{ $classname }}", result)
+ return out
+ {{- else }}
+ return NakamaAsyncResult.new()
+ {{- end}}
+{{- end }}
+{{- end }}
+`
+
+func convertRefToClassName(input string) (className string) {
+ cleanRef := strings.TrimPrefix(input, "#/definitions/")
+ className = strings.Title(cleanRef)
+ return
+}
+
+func stripNewlines(input string) (output string) {
+ output = strings.Replace(input, "\n", " ", -1)
+ return
+}
+
+func prependParameter(input string) (output string) {
+ output = "p_" + pascalToSnake(input)
+ return
+}
+
+func pascalToSnake(input string) (output string) {
+ output = ""
+ prev_low := false
+ for _, v := range input {
+ is_cap := v >= 'A' && v <= 'Z'
+ is_low := v >= 'a' && v <= 'z'
+ if is_cap && prev_low {
+ output = output + "_"
+ }
+ output += strings.ToLower(string(v))
+ prev_low = is_low
+ }
+ return
+}
+
+func apiFuncName(input string) (output string) {
+ output = pascalToSnake(input[7:])
+ return
+}
+
+func godotType(p_type string, p_ref string, p_item_type string, p_extra string, p_is_enum bool) (out string) {
+
+ is_array := false
+ is_dict := false
+ switch p_type {
+ case "integer":
+ out = "int"
+ case "string":
+ out = "String"
+ case "boolean":
+ out = "bool"
+ case "array":
+ is_array = true
+ case "object":
+ is_dict = true
+ default:
+ if p_is_enum {
+ out = "int"
+ } else {
+ out = convertRefToClassName(p_ref)
+ }
+ }
+
+ if is_array {
+ switch p_item_type {
+ case "integer":
+ out = "PackedIntArray"
+ return
+ case "string":
+ out = "PackedStringArray"
+ return
+ case "boolean":
+ out = "PackedIntArray"
+ return
+ default:
+ out = "Array"
+ }
+ }
+ if is_dict {
+ out = "Dictionary"
+ }
+ return
+}
+
+func godotDef(p_type string) (out string) {
+ switch(p_type) {
+ case "bool": out = "false"
+ case "int": out = "0"
+ case "String": out = "\"\""
+ case "PackedIntArray": out = "PackedIntArray()"
+ case "PackedStringArray": out = "PackedStringArray()"
+ case "Array": out = "Array()"
+ case "Dictionary": out = "Dictionary()"
+ }
+ return
+}
+
+func godotLooseType(p_type string) (out string) {
+ switch(p_type) {
+ case "PackedStringArray", "PackedIntArray":
+ out = "Array"
+ default:
+ out = p_type
+ }
+ return
+}
+
+func godotSchemaType(p_type string) (out string) {
+ out = "TYPE_"
+ switch(p_type) {
+ case "bool": out += "BOOL"
+ case "int": out += "INT"
+ case "String": out += "STRING"
+ case "PackedIntArray": out += "ARRAY"
+ case "PackedStringArray": out += "ARRAY"
+ case "Array": out += "ARRAY"
+ case "Dictionary": out += "DICTIONARY"
+ default: out = "\"" + p_type + "\""
+ }
+ return
+}
+
+func pascalToCamel(input string) (camelCase string) {
+ if input == "" {
+ return ""
+ }
+
+ camelCase = strings.ToLower(string(input[0]))
+ camelCase += string(input[1:])
+ return camelCase
+}
+
+func camelToPascal(camelCase string) (pascalCase string) {
+
+ if len(camelCase) <= 0 {
+ return ""
+ }
+
+ pascalCase = strings.ToUpper(string(camelCase[0])) + camelCase[1:]
+ return
+}
+
+func enumSummary(def Definition) string {
+ // quirk of swagger generation: if enum doesn't have a title
+ // then the title can be found as the first entry in the split description.
+ if def.Title != "" {
+ return def.Title
+ }
+
+ split := strings.Split(def.Description, "\n")
+
+ if len(split) <= 0 {
+ panic("No newlines in enum description found.")
+ }
+
+ return split[0]
+}
+
+func enumDescriptions(def Definition) (output []string) {
+
+ split := strings.Split(def.Description, "\n")
+
+ if len(split) <= 0 {
+ panic("No newlines in enum description found.")
+ }
+
+ if def.Title != "" {
+ return split
+ }
+
+ // quirk of swagger generation: if enum doesn't have a title
+ // then the title can be found as the first entry in the split description.
+ // so ignore for individual enum descriptions.
+ return split[2:]
+}
+
+type Definition struct {
+ Properties map[string]struct {
+ Type string
+ Ref string `json:"$ref"` // used with object
+ Items struct { // used with type "array"
+ Type string
+ Ref string `json:"$ref"`
+ }
+ AdditionalProperties struct {
+ Type string // used with type "map"
+ }
+ Format string // used with type "boolean"
+ Description string
+ }
+ Enum []string
+ Description string
+ // used only by enums
+ Title string
+}
+
+func godotClassUtils(p_name string) string {
+ if val, ok := utilities[p_name]; ok {
+ return val
+ }
+ return ""
+}
+
+func main() {
+ // Argument flags
+ var output = flag.String("output", "", "The output for generated code.")
+ flag.Parse()
+
+ inputs := flag.Args()
+ if len(inputs) < 1 {
+ fmt.Printf("No input file found: %s\n\n", inputs)
+ fmt.Println("openapi-gen [flags] inputs...")
+ flag.PrintDefaults()
+ return
+ }
+
+ input := inputs[0]
+ content, err := ioutil.ReadFile(input)
+ if err != nil {
+ fmt.Printf("Unable to read file: %s\n", err)
+ return
+ }
+
+ var schema struct {
+ Paths map[string]map[string]struct {
+ Summary string
+ OperationId string
+ Responses struct {
+ Ok struct {
+ Schema struct {
+ Ref string `json:"$ref"`
+ }
+ } `json:"200"`
+ }
+ Parameters []struct {
+ Name string
+ In string
+ Required bool
+ Type string // used with primitives
+ Items struct { // used with type "array"
+ Type string
+ }
+ Schema struct { // used with http body
+ Type string
+ Ref string `json:"$ref"`
+ }
+ Format string // used with type "boolean"
+ }
+ Security []map[string][]struct {
+ }
+ }
+ Definitions map[string]Definition
+ }
+
+ if err := json.Unmarshal(content, &schema); err != nil {
+ fmt.Printf("Unable to decode input %s : %s\n", input, err)
+ return
+ }
+
+ fmap := template.FuncMap{
+ "cleanRef": convertRefToClassName,
+ "stripNewlines": stripNewlines,
+ "title": strings.Title,
+ "uppercase": strings.ToUpper,
+ "prependParameter": prependParameter,
+ "pascalToSnake": pascalToSnake,
+ "apiFuncName": apiFuncName,
+ "godotType": godotType,
+ "godotLooseType": godotLooseType,
+ "godotSchemaType": godotSchemaType,
+ "godotDef": godotDef,
+ "isRefToEnum": func(ref string) bool {
+ if len(ref) == 0 {
+ return false
+ }
+ // swagger schema definition keys have inconsistent casing
+ var camelOk bool
+ var pascalOk bool
+ var enums []string
+
+ asCamel := pascalToCamel(ref)
+ if _, camelOk = schema.Definitions[asCamel]; camelOk {
+ enums = schema.Definitions[asCamel].Enum
+ }
+
+ asPascal := camelToPascal(ref)
+ if _, pascalOk = schema.Definitions[asPascal]; pascalOk {
+ enums = schema.Definitions[asPascal].Enum
+ }
+
+ if !pascalOk && !camelOk {
+ fmt.Printf("no definition found: %v", ref)
+ return false
+ }
+
+ return len(enums) > 0
+ },
+ "enumDescriptions": enumDescriptions,
+ "enumSummary": enumSummary,
+ "godotClassUtils": godotClassUtils,
+ }
+ tmpl, err := template.New(input).Funcs(fmap).Parse(codeTemplate)
+ if err != nil {
+ fmt.Printf("Template parse error: %s\n", err)
+ return
+ }
+
+ if len(*output) < 1 {
+ tmpl.Execute(os.Stdout, schema)
+ return
+ }
+
+ f, err := os.Create(*output)
+ if err != nil {
+ fmt.Printf("Unable to create file: %s\n", err)
+ return
+ }
+ defer f.Close()
+
+ writer := bufio.NewWriter(f)
+ tmpl.Execute(writer, schema)
+ writer.Flush()
+}
diff --git a/game/icon.svg b/game/icon.svg
new file mode 100644
index 0000000..c6bbb7d
--- /dev/null
+++ b/game/icon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>
diff --git a/game/icon.svg.import b/game/icon.svg.import
new file mode 100644
index 0000000..eed240d
--- /dev/null
+++ b/game/icon.svg.import
@@ -0,0 +1,43 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bgsw3ydxujh42"
+path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://icon.svg"
+dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/uastc_level=0
+compress/rdo_quality_loss=0.0
+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/channel_remap/red=0
+process/channel_remap/green=1
+process/channel_remap/blue=2
+process/channel_remap/alpha=3
+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/game/main.gd b/game/main.gd
new file mode 100644
index 0000000..38c4d54
--- /dev/null
+++ b/game/main.gd
@@ -0,0 +1,4 @@
+extends Node
+
+func _ready():
+ pass
diff --git a/game/main.gd.uid b/game/main.gd.uid
new file mode 100644
index 0000000..909550f
--- /dev/null
+++ b/game/main.gd.uid
@@ -0,0 +1 @@
+uid://c2dt7ss7urgy3
diff --git a/game/main.tscn b/game/main.tscn
new file mode 100644
index 0000000..91a0195
--- /dev/null
+++ b/game/main.tscn
@@ -0,0 +1,6 @@
+[gd_scene format=3 uid="uid://c5dscglj3fwm3"]
+
+[ext_resource type="Script" uid="uid://c2dt7ss7urgy3" path="res://main.gd" id="1_ig7tw"]
+
+[node name="Node" type="Node" unique_id=1975473799]
+script = ExtResource("1_ig7tw")
diff --git a/game/project.godot b/game/project.godot
new file mode 100644
index 0000000..949e671
--- /dev/null
+++ b/game/project.godot
@@ -0,0 +1,28 @@
+; Engine configuration file.
+; It's best edited using the editor UI and not directly,
+; since the parameters that go here are not all obvious.
+;
+; Format:
+; [section] ; section goes between []
+; param=value ; assign values to parameters
+
+config_version=5
+
+[application]
+
+config/name="Echoes of Vaelun"
+run/main_scene="uid://c5dscglj3fwm3"
+config/features=PackedStringArray("4.6", "Forward Plus")
+config/icon="res://icon.svg"
+
+[autoload]
+
+Nakama="*uid://b6tpsu78xwjth"
+
+[physics]
+
+3d/physics_engine="Jolt Physics"
+
+[rendering]
+
+rendering_device/driver.windows="d3d12"
diff --git a/game/test_suite/base_test.gd b/game/test_suite/base_test.gd
new file mode 100644
index 0000000..ca9dd21
--- /dev/null
+++ b/game/test_suite/base_test.gd
@@ -0,0 +1,133 @@
+extends Node
+
+class Log:
+
+ enum {ERROR, INFO, DEBUG}
+
+ static func _s(lvl):
+ if lvl == ERROR:
+ return "ERROR"
+ elif lvl == INFO:
+ return "INFO"
+ elif lvl == DEBUG:
+ return "DEBUG"
+ return "WTF"
+
+ static func __log(lvl, msg, data):
+ print("======= %s: %s" % [_s(lvl), msg])
+ if not data.is_empty():
+ var json = JSON.new()
+ print(json.stringify(data, " ", true))
+
+ static func error(msg, data={}):
+ __log(ERROR, msg, data)
+
+ static func info(msg, data={}):
+ __log(INFO, msg, data)
+
+ static func debug(msg, data={}):
+ __log(DEBUG, msg, data)
+
+const REAL_ASSERT = false
+
+var _success = false
+var _start_time = 0
+var _quit = false
+var _disabled = false
+var __me = ""
+
+# Override this to specify test initialization (called on _ready if not disabled)
+# You can run your whole test here, and call done() when finished
+func setup():
+ pass
+
+# Override this to do cleanup, make assertions at the end of the test (called on _exit_tree)
+# NOTE: You can still fail here if you like with fail() or by failing an assertion
+func teardown():
+ pass
+
+func _init():
+ __me = get_script()
+ while __me.get_base_script() != null:
+ __me = __me.get_base_script()
+
+func _ready():
+ if _disabled:
+ set_process(false)
+ set_physics_process(false)
+ set_process_input(false)
+ set_process_unhandled_input(false)
+ set_process_unhandled_key_input(false)
+ done()
+ Log.info("SKIP: %s" % __get_source(self))
+ return
+ _start_time = Time.get_ticks_usec()
+ Log.info("RUNNING: %s" % __get_source(self))
+ setup()
+
+func _exit_tree():
+ if _disabled:
+ return
+
+ teardown()
+ Log.info("%s: %s" % ["SUCCESS" if _success else "!!!!!!!!!!!!!!!!!!! FAILURE", __get_source(self)])
+
+func __get_source(who):
+ if who == null or who.get_script() == null:
+ return "Unknown source: %s" % str(who)
+ return who.get_script().resource_path
+
+func __get_caller():
+ for s in get_stack():
+ if __me.resource_path != s.source:
+ return s
+ return null
+
+func __get_assertion():
+ var stack = get_stack()
+ stack.reverse()
+ for s in stack:
+ if __me.resource_path == s.source:
+ return s
+ return null
+
+func __assert(v1, v2):
+ if not (v1 == v2):
+ Log.error("Assert Failed", {"caller": __get_caller(), "assertion": __get_assertion(), "full_stack": get_stack()})
+ print_tree_pretty()
+ _quit = true
+ if REAL_ASSERT:
+ assert(v1 == v2)
+ if v1 == v2:
+ return false
+ return true
+
+func assert_time(max_time):
+ __assert(max_time > float(Time.get_ticks_usec() - _start_time) / 1000.0 / 1000.0, true)
+
+### Returns true if the assertion failed, so you can do:
+### if assert_cond(cond):
+### return # Assertion failed!
+func assert_cond(cond):
+ return __assert(cond, true)
+
+func assert_false(cond):
+ return __assert(cond, false)
+
+func assert_equal(v1, v2):
+ return __assert(v1, v2)
+
+func done():
+ _success = true
+ _quit = true
+
+func fail():
+ _success = false
+ _quit = true
+ __assert(false, true)
+
+func disable():
+ _disabled = true
+
+func is_disabled():
+ return _disabled
diff --git a/game/test_suite/bin/.gitignore b/game/test_suite/bin/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/game/test_suite/bin/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/game/test_suite/project.godot b/game/test_suite/project.godot
new file mode 100644
index 0000000..2dd4d83
--- /dev/null
+++ b/game/test_suite/project.godot
@@ -0,0 +1,119 @@
+; Engine configuration file.
+; It's best edited using the editor UI and not directly,
+; since the parameters that go here are not all obvious.
+;
+; Format:
+; [section] ; section goes between []
+; param=value ; assign values to parameters
+
+config_version=5
+
+_global_script_classes=[{
+"base": "RefCounted",
+"class": &"NakamaAPI",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/api/NakamaAPI.gd"
+}, {
+"base": "RefCounted",
+"class": &"NakamaAsyncResult",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd"
+}, {
+"base": "RefCounted",
+"class": &"NakamaClient",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/client/NakamaClient.gd"
+}, {
+"base": "RefCounted",
+"class": &"NakamaException",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/utils/NakamaException.gd"
+}, {
+"base": "Node",
+"class": &"NakamaHTTPAdapter",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd"
+}, {
+"base": "RefCounted",
+"class": &"NakamaLogger",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/utils/NakamaLogger.gd"
+}, {
+"base": "RefCounted",
+"class": &"NakamaMultiplayerBridge",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/utils/NakamaMultiplayerBridge.gd"
+}, {
+"base": "MultiplayerPeerExtension",
+"class": &"NakamaMultiplayerPeer",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/utils/NakamaMultiplayerPeer.gd"
+}, {
+"base": "NakamaAsyncResult",
+"class": &"NakamaRTAPI",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd"
+}, {
+"base": "RefCounted",
+"class": &"NakamaRTMessage",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd"
+}, {
+"base": "RefCounted",
+"class": &"NakamaSerializer",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd"
+}, {
+"base": "NakamaAsyncResult",
+"class": &"NakamaSession",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/api/NakamaSession.gd"
+}, {
+"base": "RefCounted",
+"class": &"NakamaSocket",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/socket/NakamaSocket.gd"
+}, {
+"base": "Node",
+"class": &"NakamaSocketAdapter",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd"
+}, {
+"base": "RefCounted",
+"class": &"NakamaStorageObjectId",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd"
+}, {
+"base": "RefCounted",
+"class": &"NakamaWriteStorageObject",
+"language": &"GDScript",
+"path": "res://addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd"
+}]
+_global_script_class_icons={
+"NakamaAPI": "",
+"NakamaAsyncResult": "",
+"NakamaClient": "",
+"NakamaException": "",
+"NakamaHTTPAdapter": "",
+"NakamaLogger": "",
+"NakamaMultiplayerBridge": "",
+"NakamaMultiplayerPeer": "",
+"NakamaRTAPI": "",
+"NakamaRTMessage": "",
+"NakamaSerializer": "",
+"NakamaSession": "",
+"NakamaSocket": "",
+"NakamaSocketAdapter": "",
+"NakamaStorageObjectId": "",
+"NakamaWriteStorageObject": ""
+}
+
+[application]
+
+run/main_scene="res://tester.tscn"
+config/features=PackedStringArray("4.0")
+
+[autoload]
+
+Nakama="*res://addons/com.heroiclabs.nakama/Nakama.gd"
+Config="*res://utils/config.gd"
diff --git a/game/test_suite/run_tests.sh b/game/test_suite/run_tests.sh
new file mode 100644
index 0000000..f3a2ba4
--- /dev/null
+++ b/game/test_suite/run_tests.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+END_STRING="======= TESTS END"
+PROJECT_PATH="test_suite/"
+GODOT_BIN="test_suite/bin/godot.elf"
+OUT=`$GODOT_BIN --headless --debug --path test_suite/ -s res://runner.gd`
+RUN=`echo $OUT | grep "$END_STRING"`
+RES=`echo $OUT | grep FAILURE`
+
+echo "$OUT"
+
+if [ -z "$RUN" ]; then
+ echo "Run failed!"
+ exit 1
+fi
+
+if [ -z "$RES" ]; then
+ echo "Tests passed!"
+ exit 0
+fi
+echo "Tests failed!"
+exit 1
diff --git a/game/test_suite/runner.gd b/game/test_suite/runner.gd
new file mode 100644
index 0000000..46a0dcb
--- /dev/null
+++ b/game/test_suite/runner.gd
@@ -0,0 +1,15 @@
+extends SceneTree
+
+func _init():
+ var me = ProjectSettings.localize_path(ProjectSettings.globalize_path(get_script().resource_path))
+ var dir = ProjectSettings.localize_path(me.get_base_dir())
+
+ if dir != "res://":
+ print("Must be run with the script dir as the res:// path")
+ print("Current path: ", ProjectSettings.globalize_path(dir))
+ print("RES:// path: ", ProjectSettings.globalize_path("res://"))
+ quit()
+ return
+ var n = load("res://tester.gd").new()
+ root.add_child(n)
+ current_scene = n
diff --git a/game/test_suite/settings.json b/game/test_suite/settings.json
new file mode 100644
index 0000000..89d644d
--- /dev/null
+++ b/game/test_suite/settings.json
@@ -0,0 +1,7 @@
+
+{
+ "HOST": "127.0.0.1",
+ "PORT": 7350,
+ "SCHEME": "http",
+ "SERVER_KEY": "defaultkey"
+}
diff --git a/game/test_suite/tester.gd b/game/test_suite/tester.gd
new file mode 100644
index 0000000..6f9bd0d
--- /dev/null
+++ b/game/test_suite/tester.gd
@@ -0,0 +1,56 @@
+extends Node
+
+var current = null
+var tests = []
+var frame_time = 0.0
+var fixed_time = 0.0
+var dirs = ["tests"]
+
+func _ready():
+ while not dirs.is_empty():
+ var d = dirs.pop_front()
+ _expand(d, dirs)
+ print("Running %d tests:\n%s" % [tests.size(), tests])
+
+func _process(delta):
+ frame_time += delta
+ _check_end()
+
+func _physics_process(delta):
+ fixed_time += delta
+ _check_end()
+
+func _check_end():
+ if current == null:
+ if tests.size() == 0:
+ _end_tests()
+ return
+ current = load(tests.pop_front()).new()
+ add_child(current)
+ elif current._quit:
+ if not current._success:
+ _end_tests()
+ current.queue_free()
+ current = null
+
+func _end_tests():
+ print("======= TESTS END")
+ set_process(false)
+ set_physics_process(false)
+ await get_tree().create_timer(1.0).timeout
+ get_tree().call_deferred("quit")
+
+func _expand(p_name, r_dirs):
+ var dir = DirAccess.open("res://")
+ if dir.change_dir(p_name) != OK:
+ print("Unable to chdir into: %s" % p_name)
+ return
+ dir.list_dir_begin()
+ var f = dir.get_next()
+ while f != "":
+ if dir.current_is_dir():
+ r_dirs.append("%s/%s" % [p_name, f])
+ if f.ends_with("_test.gd"):
+ tests.append("%s/%s" % [p_name, f])
+ f = dir.get_next()
+ dir.list_dir_end()
diff --git a/game/test_suite/tester.tscn b/game/test_suite/tester.tscn
new file mode 100644
index 0000000..492c551
--- /dev/null
+++ b/game/test_suite/tester.tscn
@@ -0,0 +1,12 @@
+[gd_scene load_steps=2 format=3 uid="uid://bglgahpqv1ojs"]
+
+[ext_resource type="Script" path="res://tester.gd" id="1"]
+
+[node name="Control" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("1")
diff --git a/game/test_suite/tests/client_test.gd b/game/test_suite/tests/client_test.gd
new file mode 100644
index 0000000..a727229
--- /dev/null
+++ b/game/test_suite/tests/client_test.gd
@@ -0,0 +1,32 @@
+extends "res://base_test.gd"
+
+func setup():
+ var username = str(randi_range(1000, 100000))
+ var client = Nakama.create_client(Config.SERVER_KEY, Config.HOST, Config.PORT, Config.SCHEME)
+ # POST
+ var session = await client.authenticate_custom_async("MyIdentifier")
+ if assert_cond(session.is_valid()) or assert_cond(!session.is_expired()):
+ return
+ # PUT
+ var update = await client.update_account_async(session, username)
+ if assert_false(update.is_exception()):
+ return
+ # GET
+ var account : NakamaAPI.ApiAccount = await client.get_account_async(session)
+ if assert_false(account.is_exception()):
+ return
+ if assert_cond(account.user.username == username):
+ return
+ # POST - DELETE
+ var group : NakamaAPI.ApiGroup = await client.create_group_async(session, "MyGroupName3")
+ if assert_false(group.is_exception()):
+ return
+ var delete = await client.delete_group_async(session, group.id)
+ if assert_false(delete.is_exception()):
+ return
+ # All good
+ done()
+ return
+
+func _process(_delta):
+ assert_time(3)
diff --git a/game/test_suite/tests/socket_multi_test.gd b/game/test_suite/tests/socket_multi_test.gd
new file mode 100644
index 0000000..30454a4
--- /dev/null
+++ b/game/test_suite/tests/socket_multi_test.gd
@@ -0,0 +1,79 @@
+extends "res://base_test.gd"
+
+var content = {"My": "message"}
+var match_string_props = {"region": "europe"}
+var match_numeric_props = {"rank": 8}
+var got_msg = false
+var got_match = false
+var socket1 = null
+var socket2 = null
+
+func setup():
+ var client = Nakama.create_client(Config.SERVER_KEY, Config.HOST, Config.PORT, Config.SCHEME)
+
+ var session1 = await client.authenticate_custom_async("MyIdentifier")
+ if assert_cond(session1.is_valid()):
+ return
+
+ var session2 = await client.authenticate_custom_async("MyIdentifier2")
+ if assert_cond(session1.is_valid()):
+ return
+
+ socket1 = Nakama.create_socket_from(client)
+ socket1.received_channel_message.connect(self._on_socket1_message)
+ socket1.received_matchmaker_matched.connect(self._on_socket1_matchmaker_matched)
+ var done = await socket1.connect_async(session1)
+ # Check that connection succeded
+ if assert_false(done.is_exception()):
+ return
+
+ socket2 = Nakama.create_socket_from(client)
+ done = await socket2.connect_async(session2)
+ # Check that connection succeded
+ if assert_false(done.is_exception()):
+ return
+
+ # Join room
+ var room1 = await socket1.join_chat_async("MyRoom", NakamaSocket.ChannelType.Room)
+ if assert_false(room1.is_exception()):
+ return
+ var room2 = await socket2.join_chat_async("MyRoom", NakamaSocket.ChannelType.Room)
+ if assert_false(room2.is_exception()):
+ return
+
+ # Socket 2 send message to socket 1
+ var msg_ack = await socket2.write_chat_message_async(room2.id, content)
+ if assert_false(msg_ack.is_exception()):
+ return
+
+ var ticket1 = await socket1.add_matchmaker_async("+properties.region:europe +properties.rank:>=7 +properties.rank:<=9", 2, 8, match_string_props, match_numeric_props)
+ if assert_false(ticket1.is_exception()):
+ return
+ var ticket2 = await socket2.add_matchmaker_async("+properties.region:europe +properties.rank:>=7 +properties.rank:<=9", 2, 8, match_string_props, match_numeric_props)
+ if assert_false(ticket2.is_exception()):
+ return
+
+func _on_socket1_message(msg):
+ if assert_equal(msg.content, JSON.stringify(content)):
+ return
+ got_msg = true
+ check_end()
+
+func _on_socket1_matchmaker_matched(p_matchmaker_matched):
+ if assert_equal(JSON.stringify(p_matchmaker_matched.users[0].string_properties), JSON.stringify(match_string_props)):
+ return
+ if assert_equal(JSON.stringify(p_matchmaker_matched.users[0].numeric_properties), JSON.stringify(match_numeric_props)):
+ return
+ if assert_equal(JSON.stringify(p_matchmaker_matched.users[1].string_properties), JSON.stringify(match_string_props)):
+ return
+ if assert_equal(JSON.stringify(p_matchmaker_matched.users[1].numeric_properties), JSON.stringify(match_numeric_props)):
+ return
+ got_match = true
+ check_end()
+
+func _process(_delta):
+ assert_time(60)
+
+func check_end():
+ if got_match and got_msg:
+ done()
diff --git a/game/test_suite/tests/socket_party_test.gd b/game/test_suite/tests/socket_party_test.gd
new file mode 100644
index 0000000..05a992b
--- /dev/null
+++ b/game/test_suite/tests/socket_party_test.gd
@@ -0,0 +1,98 @@
+extends "res://base_test.gd"
+
+var content = {"My": "message"}
+var match_props = {"region": "europe"}
+var got_msg = false
+var got_match = false
+var socket1 : NakamaSocket = null
+var socket2 : NakamaSocket = null
+
+func setup():
+ var client = Nakama.create_client(Config.SERVER_KEY, Config.HOST, Config.PORT, Config.SCHEME)
+
+ var session1 = await client.authenticate_custom_async("MyIdentifier")
+ if assert_cond(session1.is_valid()):
+ return
+
+ var session2 = await client.authenticate_custom_async("MyIdentifier2")
+ if assert_cond(session2.is_valid()):
+ return
+
+ socket1 = Nakama.create_socket_from(client)
+ socket1.received_party_close.connect(self._on_party_close)
+ socket1.received_party_data.connect(self._on_party_data)
+ socket1.received_party_join_request.connect(self._on_party_join_request)
+
+ var conn = await socket1.connect_async(session1)
+ # Check that connection succeded
+ if assert_false(conn.is_exception()):
+ return
+
+ var party = await socket1.create_party_async(false, 2)
+ if assert_false(party.is_exception()):
+ return
+ #done()
+
+ socket2 = Nakama.create_socket_from(client)
+ socket2.received_party.connect(self._on_party)
+ socket2.received_party_close.connect(self._on_party_close)
+ socket2.received_party_join_request.connect(self._on_party_join_request)
+ socket2.received_party_leader.connect(self._on_party_leader)
+ socket2.received_party_presence.connect(self._on_party_presence)
+
+ var conn2 = await socket2.connect_async(session2)
+ # Check that connection succeded
+ if assert_false(conn2.is_exception()):
+ return
+
+ var join = await socket2.join_party_async(party.party_id)
+ if assert_false(join.is_exception()):
+ return
+
+func _on_party_join_request(party_join_request : NakamaRTAPI.PartyJoinRequest):
+ prints("_on_party_join_request", party_join_request)
+ var requests : NakamaRTAPI.PartyJoinRequest = await socket1.list_party_join_requests_async(party_join_request.party_id)
+ if assert_false(requests.is_exception()):
+ return
+ if assert_cond(requests.presences.size() == 1):
+ return
+
+ await socket1.accept_party_member_async(party_join_request.party_id, party_join_request.presences[0])
+ await socket1.promote_party_member(party_join_request.party_id, party_join_request.presences[0])
+
+func _on_party(party):
+ prints("_on_party", party)
+
+func _on_party_close(party_close):
+ prints("_on_party_close", party_close)
+
+func _on_party_data(data : NakamaRTAPI.PartyData):
+ prints("_on_party_data", data)
+ var left = await socket1.leave_party_async(data.party_id)
+ if assert_false(left.is_exception()):
+ return
+
+func _on_party_leader(party_leader : NakamaRTAPI.PartyLeader):
+ prints("_on_party_leader", party_leader)
+ var ticket = await socket2.add_matchmaker_party_async(party_leader.party_id)
+ if assert_false(ticket.is_exception()):
+ return
+ _on_party_ticket(ticket)
+
+func _on_party_ticket(ticket : NakamaRTAPI.PartyMatchmakerTicket):
+ prints("_on_party_ticket", ticket)
+ var removed = await socket2.remove_matchmaker_party_async(ticket.party_id, ticket.ticket)
+ if assert_false(removed.is_exception()):
+ return
+ var sent = await socket2.send_party_data_async(ticket.party_id, 1, "asd")
+ if assert_false(sent.is_exception()):
+ return
+
+func _on_party_presence(party_presence : NakamaRTAPI.PartyPresenceEvent):
+ prints("_on_party_presence", party_presence)
+ var left = party_presence.leaves.size() == 1
+ if left:
+ var closed = await socket2.close_party_async(party_presence.party_id)
+ if assert_false(closed.is_exception()):
+ return
+ done()
diff --git a/game/test_suite/tests/socket_simple_test.gd b/game/test_suite/tests/socket_simple_test.gd
new file mode 100644
index 0000000..b25c2d5
--- /dev/null
+++ b/game/test_suite/tests/socket_simple_test.gd
@@ -0,0 +1,27 @@
+extends "res://base_test.gd"
+
+var _connected = false
+
+func setup():
+ var client = Nakama.create_client(Config.SERVER_KEY, Config.HOST, Config.PORT, Config.SCHEME)
+
+ var session = await client.authenticate_custom_async("MyIdentifier")
+ if assert_cond(session.is_valid()):
+ return
+
+ var socket = Nakama.create_socket_from(client)
+ socket.connected.connect(self._on_socket_connected)
+ var conn = await socket.connect_async(session)
+ # Check that connection succeded
+ if assert_false(conn.is_exception()):
+ return
+ # Check that signal has been called
+ if assert_cond(_connected):
+ return
+ done()
+
+func _on_socket_connected():
+ _connected = true
+
+func _process(_delta):
+ assert_time(3)
diff --git a/game/test_suite/tests/storage_test.gd b/game/test_suite/tests/storage_test.gd
new file mode 100644
index 0000000..e13fd8d
--- /dev/null
+++ b/game/test_suite/tests/storage_test.gd
@@ -0,0 +1,72 @@
+extends "res://base_test.gd"
+
+const COLLECTION = "ACollection"
+const K1 = "Question"
+const K2 = "Answer"
+const V1 = {"question": "Ultimate question"}
+const V2 = {"answer": "42"}
+
+func setup():
+ var username = str(randi_range(1000, 100000))
+ var client = Nakama.create_client(Config.SERVER_KEY, Config.HOST, Config.PORT, Config.SCHEME)
+
+ var session = await client.authenticate_custom_async("MyIdentifier")
+ if assert_cond(session.is_valid()):
+ return
+
+ var objs = [
+ NakamaWriteStorageObject.new(COLLECTION, K1, 1, 1, to_json(V1), ""),
+ NakamaWriteStorageObject.new(COLLECTION, K2, 1, 1, to_json(V2), "")
+ ]
+ var write : NakamaAPI.ApiStorageObjectAcks = await client.write_storage_objects_async(session, objs)
+ if assert_false(write.is_exception()):
+ return
+
+ var objs_ids = []
+ for a in write.acks:
+ #var obj : NakamaAPI.ApiStorageObjectAck = a
+ objs_ids.append(NakamaStorageObjectId.new(a.collection, a.key, a.user_id, a.version))
+
+ var read : NakamaAPI.ApiStorageObjects = await client.read_storage_objects_async(session, objs_ids)
+ if assert_false(read.is_exception()):
+ return
+ if assert_equal(read.objects.size(), 2):
+ return
+ if assert_equal(read.objects[0].collection, COLLECTION):
+ return
+ if assert_cond(read.objects[0].key in [K1, K2]):
+ return
+ if assert_cond(to_json(parse_json(read.objects[0].value)) in [to_json(V1), to_json(V2)]):
+ return
+
+ # Delete one
+ var del = await client.delete_storage_objects_async(session, [objs_ids[0]])
+ if assert_false(del.is_exception()):
+ return
+
+ # Confirm that one was deleted
+ var read2 : NakamaAPI.ApiStorageObjects = await client.read_storage_objects_async(session, objs_ids)
+ if assert_false(read2.is_exception()):
+ return
+ if assert_equal(read2.objects.size(), 1):
+ return
+ if assert_equal(read2.objects[0].collection, COLLECTION):
+ return
+ if assert_equal(read2.objects[0].key, objs_ids[1].key):
+ return
+ if assert_cond(to_json(parse_json(read2.objects[0].value)) in [to_json(V1), to_json(V2)]):
+ return
+ done()
+ return
+
+func _process(_delta):
+ assert_time(3)
+
+func to_json(value) -> String:
+ return JSON.stringify(value)
+
+func parse_json(value):
+ var json = JSON.new()
+ if json.parse(value) != OK:
+ return null
+ return json.get_data()
diff --git a/game/test_suite/utils/config.gd b/game/test_suite/utils/config.gd
new file mode 100644
index 0000000..edf3cfd
--- /dev/null
+++ b/game/test_suite/utils/config.gd
@@ -0,0 +1,22 @@
+extends Node
+
+var HOST = "127.0.0.1"
+var PORT = 7350
+var SCHEME = "http"
+var SERVER_KEY = "defaultkey"
+
+func _ready():
+ var f = FileAccess.open("res://settings.json", FileAccess.READ)
+ if not f:
+ return
+ var json = JSON.new()
+ var error = json.parse(f.get_as_text())
+ var parsed = json.get_data()
+ if error != OK or typeof(parsed) != TYPE_DICTIONARY:
+ return
+ for k in parsed:
+ match k:
+ "HOST": HOST = parsed[k]
+ "PORT": PORT = parsed[k]
+ "SCHEME": SCHEME = parsed[k]
+ "SERVER_KEY": SERVER_KEY = parsed[k]
diff --git a/toolchain/docker-compose.yml b/toolchain/docker-compose.yml
new file mode 100644
index 0000000..205230c
--- /dev/null
+++ b/toolchain/docker-compose.yml
@@ -0,0 +1,82 @@
+version: "3"
+services:
+ cockroachdb:
+ image: cockroachdb/cockroach:latest-v23.1
+ command: start-single-node --insecure --store=attrs=ssd,path=/var/lib/cockroach/
+ restart: "no"
+ volumes:
+ - data:/var/lib/cockroach
+ expose:
+ - "8080"
+ - "26257"
+ ports:
+ - "26257:26257"
+ - "8080:8080"
+ healthcheck:
+ test: ["CMD", "curl", "-f", "http://localhost:8080/health?ready=1"]
+ interval: 3s
+ timeout: 3s
+ retries: 5
+
+ nakama:
+ image: registry.heroiclabs.com/heroiclabs/nakama:3.22.0
+ entrypoint:
+ - "/bin/sh"
+ - "-ecx"
+ - >
+ /nakama/nakama migrate up --database.address root@cockroachdb:26257 &&
+ exec /nakama/nakama --name nakama1 --database.address root@cockroachdb:26257
+ --logger.level DEBUG --session.token_expiry_sec 7200
+ --metrics.prometheus_port 9100
+ restart: "no"
+ links:
+ - "cockroachdb:db"
+ depends_on:
+ cockroachdb:
+ condition: service_healthy
+ prometheus:
+ condition: service_started
+ volumes:
+ - ../data/nakama:/nakama/data
+ expose:
+ - "7349"
+ - "7350"
+ - "7351"
+ - "9100"
+ ports:
+ - "7349:7349"
+ - "7350:7350"
+ - "7351:7351"
+ healthcheck:
+ test: ["CMD", "/nakama/nakama", "healthcheck"]
+ interval: 10s
+ timeout: 5s
+ retries: 5
+
+ prometheus:
+ image: prom/prometheus
+ entrypoint: /bin/sh -c
+ command: |
+ 'sh -s <<EOF
+ cat > ./prometheus.yml <<EON
+ global:
+ scrape_interval: 15s
+ evaluation_interval: 15s
+
+ scrape_configs:
+ - job_name: prometheus
+ static_configs:
+ - targets: ['localhost:9090']
+
+ - job_name: nakama
+ metrics_path: /
+ static_configs:
+ - targets: ['nakama:9100']
+ EON
+ prometheus --config.file=./prometheus.yml
+ EOF'
+ ports:
+ - "9090:9090"
+
+volumes:
+ data: