Srayan Jana
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# WebRTC architecture This is for Snopek Games' WebRTC with Nakama demo. NON EXHAUSTIVE, EDITS WELCOME ## Singletons any script in the project can access all of these files ### Build useless, only used by GitLab CI when building the game ### GameState for now, just tells your if the game is in Local or Online mode ### Nakama a singleton for communicating with a Nakama server - part of the Nakama client library from Heroic Labs. Don't change unless you need an uncommitted patch to the upstream library. ### OnlineMatch sets up all of your webrtc stuff. Don't touch this either, unless you know what your doing. (TODO: Go back and document how all of this works, theres a lot of important stuff in here) (I haven't covered everything in here yet, but I want to.) Exposes these variables: - Config variables - min_players, max_players - client_version - ice_servers - This seems to contain data relating to STUN servers the project uses. - Nakama variables - nakama_socket -> an object representation of the persistent connection the client has to the server. - More specifically, the socket is the "realtime client" which is a websocket connection to the Nakama server. - The actual game is run over WebRTC, but we are using the websocket to set up the matchmaking and act as a signaling server for the WebRTC to run on. - This is also where the in game chat is hosted - my_session_id, match_id -> both string representation of id's - matchmaker_ticker - WebRTC variables (variables starting with underscores aren't meant to be accessed from outside this script, they are private) - _webrtc_multiplayer - _webrtc_peers -> Dictionary - _webrtc_peers_connected -> Dictionary - Player variables - players -> Dictionary of players Networking enums: - MatchState{LOBBY, MATCHING, CONNECTING, WAITING_FOR_ENOUGH_PLAYERS, READY, PLAYING} - MatchMode{NONE, CREATE, JOIN, MATCHMAKER} - PlayerStatus{CONNECTING, CONNECTED} - MatchOpCode{WEBRTC_PEER_METHOD, JOIN_SUCCESS, JOIN_ERROR} Exposes these signals: - Error Signals - error (takes in a "message") - disconnected() - Match Signals - match_created(takes in a "match_id") - match_joined(takes in a "match_id") - matchmaker_matched(takes in "players", a dictionary of Player objects) - Player Signals - player_joined(takes in a Player object) - player_left(takes in a Player object) - player_status_changed(takes in a Player object, and also a "status object") - Ready Up Signals - match_ready(takes in "players", a dictionary of Player objects) - match_not_ready() #### Player SUPER IMPORTANT: Online Match has its own class called Player that is used for many of the signals seen above. It's purpose is to connect a Nakama user to a Godot peer, and includes within it a lot of important information. Variables: - session_id -> String: how Nakama identifies the current player session - peer_id -> int: how Godot identifies the peer in its High-level Multiplayer API - username -> String Methods: - _init(_session_id: String, _username: String, _peer_id: int) - A constuctor for the class - static from_presence(presence: NakamaRTAPI.UserPresence, _peer_id: int) - Returns a Player object built from Nakama presence data - static func from_dict(data: Dictionary) - converts a Player from it's Dictionary representation. - useful for deserialization. - to_dict() - converts a Player to it's Dictionary representation. You can turn it back with the function above. Useful for serialzation. ### Online Boilerplate networking stuff. Exposes these variables: - nakama_client -> the "request/response" client thats connecting to the server. - It makes HTTP requests to the server (any one time thing that doesn't require a constant connections) - For example, authentication requests. - nakama_session -> an object representation of a successful authentication attempt to the server. - nakama_socket -> see the explanation in OnlineMatch Implements these functions: - get_nakama_client - if it doesn't exist, it creates a client object to connect to the Nakama server, and sets nakama_client to it. - set_nakama_session: - sets nakama_session to the new session passed in. Closes out the old session for you - Emits signal "session_changed" if changed to any session. - However, emits "session_connected" only if the session is valid. - connect_nakama_socket - if the current socket isn't null or currently connecting, then create a new socket, and set the current nakama_socket to it. - emits "socket_connected" ## In-game Networking Scripts These are the scripts that, while not being tied to the actual gameplay, help set up with the networking stuff and keeps it running. Important note: There are a bunch of functions that are "remotesync" functions. What this means is that it can be called as a normal function, but it can also be called by remote clients on the local client. (Ex. Client B can call player_ready in an rpc call and say they want it called on Client A). When you rpc a remotesync function, you call it on both the local AND remote clients. ### Game Sets up the players and does match/player life cycle stuff Exposes these variables: - Player - just a reference to the (actual in game) Player - map_scene - just a packed scene - map - a reference to the actual map that the match will take place in - camera - just the in game camera - original_camera_position - camera will probably track the players, so its best to store the original location for easy setup between each rounds - game_started, game_over, both booleans, pretty self-explanatory - players_alive - a dictionary of all the players left alive in the round. - players_setup - a dictionary marking which players have reported back that they have finished setting up the game Exposes these signals for callbacks: - game_started() - player_dead(player_id) - game_over(player_id) Implements these functions: - game_start(players: Dictionary) - If its an online match, an rpc is set out to call _do_game_setup(players) on ALL CLIENTS, both local and remote - else, just _do_game_setup on the local client - _do_game_setup(players: Dictionary) - This is a remotesync function - We first pause the game to set everything up - If game_started is true, stop the game since we need to set everything up again - After that, we set game_started to true, game over is false, and players_alive = players since we're starting with a clean slate and all players should be alive - We then reload the map. - After that, we spawn in all of the (in game) player nodes, and connect them to their peer representations. We take care to make sure they each spawn in the correct location on the map. We also make sure that the function _on_player_dead(player_id) is connected to the signal player dead for each player object. - By the way, if its not an online match, we set up multiple local inputs for each local plyaer - If it is an online match, we spawn in the player node that is the local client last, as that is a special case, since we need to set up our inputs to be directed towards it as well as the local client's peer id. - After all that, we either do a normal game start(_do_game_start()) if its an offline match, or we call _finished_game_setup on the lobby host. - _finished_game_setup(player_id: int) - This is a mastersync function, which means this is ONLY called when the client is the host of the lobby. - Since this client is the host, it gets the confermation that each player that's alive is setup. - If all the players that are alive are set up, then the host calls _do_game_start() on every client including itself. - _do_game_start() - This is a remotesync function - First, it starts up the map. - Then, it sends out the signal "game_started", which will go be called in Main.gd as _on_Game_game_started() - It also unpauses the game - game_stop() - Stop the map being played, and set game_started to false. - Clear both players_setup and players_alive - It also removes all of the current player peer representations from the scene. - reload_map() - Remove the map - create a new instance of the map, in the same place as the old map in the scene tree - Reset the camera to its original position in the map. - kill_player(player_id) - get the player_node related to player_id - If it exists, call the die method on it. - If it doesn't have that, just queue free it, and call _on_player_dead(player_id) - _on_player_dead(player_id) - emit "player_dead" with player_id - erase the dead player from players_alive - if there's no players left alive, game_over = true and emit_signal "game_over" ### Main Gets you connected to Nakama and also sets up the WebRTC connections (We will ignore the UI stuff as it's not really relevant and its pretty self-explanatory.) Before we dive into this code, there are a few functions that use "get_tree().is_network_server()", so lets quickly explain what that is. - In each lobby, there should be one peer/player who is considered the "host" - this is the player with a peer id of 1. In private matches, this is the player who originally created the match - in public matches created through the matchmaker it's chosen pseudo-randomly. Besides the other players/peers they have the extra responsibility of keeping track of certain things, including players scores and starting and ending the game. Exposes these variables: - game -> an object representation of the Game script (as seen above) - players -> the list of all players currently in the server - players_ready -> the list of players ready to actually start the match. Represented as a bunch of session_id (succesful authentication) tokens. - players_score -> dictionary of the players current scores. This is only populated on the host We'll skip over most of the UI related functions, but there's one important one: _on_ReadyScreen_ready_pressed() -> void: - This sends a callback to all of the remote clients player_ready function (defined down below) and passes in the session id from OnlineMatch ### OnlineMatch callback functions - _on_OnlineMatch_error(message: String) - Connected to OnlineMatch "error" signal, just prints out an error on screen - _on_OnlineMatch_disconnected() - Connected to OnlineMatch "disconnected" signal, prints out a disconnected message - _on_OnlineMatch_player_left(player) - Connected to OnlineMatch "player_left" signal, takes in a (OnlineMatch) Player object - print out a message showing the player left - Removes the Player from the variable "game" (reference to Game.gd) - Deletes it from both players and players_ready - _on_OnlineMatch_player_status_changed(player, status) - Takes in an OnlineMatch Player object and status - if the status has changed so that the player is now connected - First check to see if we are the "host" for this lobby, which gives us permission to start the game. - If so, then we will loop through all of the current players in players_ready. - We will call player_ready() on each of them, sending in the peer_id of the player that has sucessfully connected. ### Gameplay methods and callbacks - player_ready(session_id: String) - Takes in a session_id, representing the game client that has signaled that its ready. - This is a "remotesync" function, meaning that it can be called as a normal function, but it can also be called by remote clients on the local client. (Ex. Client B can call player_ready in an rpc call and say they want it called on Client A) - We first print a message showing that we're ready - Then, we make sure that we are the "host" for this lobby and that the session_id being passed in isnt the local client. - Then, in players_ready, we set that the player with the session_id is ready (true) - We also do another check to see if players_ready is equal to the amount of players currently in the lobby. - If it is, then we se the match_state to playing if it isn't already, and we start the game (start_game()). - start_game() - If its an online game, it first sets players to the list of players stored in OnlineMatch, else just set it to some predefined defaults - Then, start the game (by calling game.game_start(players)) - stop_game() - Leave the online match (OnlineMatch.leave()) - Then, clear players, players_ready, and players_score - Finally, stop the game (game.game_stop()) - restart_game() - Just calls stop_game() and then start_game() The following are callbacks from Game to Main, but they are all declared in the scene (Main.tscn) - _on_Game_game_started() - callback from game_started() - sets up the game for playing - _on_Game_player_dead(player_id: int) - callback from player_dead(player_id) - Check to see if the game is online. - If so, get the id of the local player client (get_tree().get_network_unique_id()) and see if it's the same of the player_id passed into the function. - If it is, print a message saying that you're dead, you lost or something to that effect. - _on_Game_game_over(player_id: int) - callback from game_over(player_id) - Called on game over, player_id is the id of the player who won. - First, clear out everyone in players_ready - First check to see if this is an online game. If it is, just call show_winner, and pass in the player associated with the player_id - Else, first we check to see if we are the "host" of this lobby. - If we are, first we either set the player score of that player to 1 if it didn't exist yet, else we add it by one. - after that, then we just rpc show_winner, passing in is_match. - is_match being a boolean that checks if the game has reached match point. (TODO: Document this function more. It's late and I'm tired) - show_winner(name: String, session_id: String = '', score: int = 0, is_match: bool = false) - name is the name of the plauyer, and is_match is a boolean that checks if the game has reached match point. - This is a remotesync function - ## Gameplay scripts w/ Networking ### Map Constants: - TILE_SIZE - tile size Exposes these functions: - map_start() - Things you want to happen when the map starts. Not used in this game, but in Retro Tank Party this starts the powerup spawn timers, or in Fish Game this spawns a weapon on all the weapon spawners. - map_stop() - Things you want to happen when the map stops. Mostly, just stopping the things started in `map_start()`. - get_map_rect() - just returns the size of the map. ### Player Exposes thses variables: - player_name_label - reference to the UI label - hitbox - Hitbox reference - animation_player - animation player reference - player_controlled - boolean saying if this player node is the one being controlled by local inputs - input_prefix - tells which port the inputs are coming from. - speed - speed of the player Has these signals: - player_dead() - called when the player dies. Implements these functions: - set_player_name(player_name: String) - set's the player label text to the player name - attack() - hurts anyone who overlaps the hitbox except for the player node itself - hurt() - This is a very simple game, so the player just dies. - If its online, its a normal die, else die gets rpc to every single client. - die() - This is a remotesync function - the player node gets queue_free() and a "player_dead" signal is emitted. - _physics_process(delta: float) - if the local client is controlling the player node - Get the input vector, and move the player node with move and slide with the strength of speed - check to see if the player pressed attack and that an attacking animation is not being played. If so, play an attack animation - if its online, do an rpc call for update_remote_player with the new game state values - update_remote_player(_position: Vector2, is_attacking: bool) - updates the gamestate - This is marked as `puppet` because you only want it run on peers that aren't the master of this player (as set in `player.set_network_master()`) - Extremely naive position and animation sync'ing. - This will work locally, and under ideal network conditions, but likely won't be acceptable over the live internet for a large percentage of users. - You'll need to replace this with more efficient sync'ing mechanism, which could include input prediction, rollback, limiting how often sync'ing happens or any other number of techniques. - In addition to that, you'll also need to expand the number of things that are sync'd, depending on the needs of your game.

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully