Skip to content

Commit

Permalink
Move connection code to a top-level Game node
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanLovato committed Apr 22, 2020
1 parent 794e3ca commit 2976cf5
Show file tree
Hide file tree
Showing 24 changed files with 300 additions and 245 deletions.
2 changes: 1 addition & 1 deletion godot/project.godot
Expand Up @@ -141,7 +141,7 @@ _global_script_class_icons={
[application]

config/name="Intro to Nakama"
run/main_scene="res://src/Main/MainMenu.tscn"
run/main_scene="res://src/Main/Game.tscn"
config/icon="res://icon.png"

[autoload]
Expand Down
16 changes: 8 additions & 8 deletions godot/src/Autoload/Connection.gd
Expand Up @@ -80,7 +80,7 @@ var _authenticator := Authenticator.new(_client, _exception_handler)
var _storage_worker: StorageWorker


# Async coroutine. Authenticates a new session via email and password, and
# Asynchronous coroutine. Authenticates a new session via email and password, and
# creates a new account when it did not previously exist, then initializes _session.
# Returns OK or a nakama error code. Stores error messages in `Connection.error_message`
func register_async(email: String, password: String) -> int:
Expand All @@ -90,7 +90,7 @@ func register_async(email: String, password: String) -> int:
return result


# Async coroutine. Authenticates a new session via email and password, but will
# Asynchronous coroutine. Authenticates a new session via email and password, but will
# not try to create a new account when it did not previously exist, then
# initializes _session. If a session previously existed in `AUTH`, will try to
# recover it without needing the authentication server.
Expand All @@ -102,7 +102,7 @@ func login_async(email: String, password: String) -> int:
return result


# Async coroutine. Connects the socket to the live server.
# Asynchronous coroutine. Connects the socket to the live server.
# Returns OK or a nakama error number. Error messages are stored in `Connection.error_message`
func connect_to_server_async() -> int:
_socket = Nakama.create_socket_from(_client)
Expand Down Expand Up @@ -150,7 +150,7 @@ func get_user_id() -> String:
return ""


# Async coroutine. Joins the match representing the world and the global chat
# Asynchronous coroutine. Joins the match representing the world and the global chat
# room. Will get the match ID from the server through a remote procedure (see world_rpc.lua).
# Returns OK, a nakama error number, or ERR_UNAVAILABLE if the socket is not connected.
# Stores any error message in `Connection.error_message`
Expand Down Expand Up @@ -193,7 +193,7 @@ func join_world_async() -> int:
return parsed_result


# Async coroutine. Gets the list of characters belonging to the user out of
# Asynchronous coroutine. Gets the list of characters belonging to the user out of
# server storage.
# Returns an Array of {name: String, color: Color} dictionaries.
# Returns an empty array if there is a failure or if no characters are found.
Expand All @@ -219,15 +219,15 @@ func update_player_character_async(color: Color, name: String) -> int:
return result


# Async coroutine. Delete the character at the specified index in the array from
# Asynchronous coroutine. Delete the character at the specified index in the array from
# player storage. Returns OK, a nakama error code, or ERR_PARAMETER_RANGE_ERROR
# if the index is too large or is invalid.
func delete_player_character_async(idx: int) -> int:
var result: int = yield(_storage_worker.delete_player_character_async(idx), "completed")
return result


# Async coroutine. Get the last logged in character from the server, if any.
# Asynchronous coroutine. Get the last logged in character from the server, if any.
# Returns a {name: String, color: Color} dictionary, or an empty dictionary if no
# character is found, or something goes wrong.
func get_last_player_character_async() -> Dictionary:
Expand All @@ -237,7 +237,7 @@ func get_last_player_character_async() -> Dictionary:
return character


# Async coroutine. Put the last logged in character into player storage on the server.
# Asynchronous coroutine. Put the last logged in character into player storage on the server.
# Returns OK, or a nakama error code.
func store_last_player_character_async(name: String, color: Color) -> int:
var result: int = yield(
Expand Down
4 changes: 2 additions & 2 deletions godot/src/Autoload/Delegates/Authenticator.gd
Expand Up @@ -13,7 +13,7 @@ func _init(client: NakamaClient, exception_handler: ExceptionHandler) -> void:
_exception_handler = exception_handler


# Async coroutine. Authenticates a new session via email and password, and
# Asynchronous coroutine. Authenticates a new session via email and password, and
# creates a new account when it did not previously exist, then initializes session.
# Returns OK or a nakama error code. Stores error messages in `Connection.error_message`
func register_async(email: String, password: String) -> int:
Expand All @@ -33,7 +33,7 @@ func register_async(email: String, password: String) -> int:
return parsed_result


# Async coroutine. Authenticates a new session via email and password, but will
# Asynchronous coroutine. Authenticates a new session via email and password, but will
# not try to create a new account when it did not previously exist, then
# initializes session. If a session previously existed in `AUTH`, will try to
# recover it without needing the authentication server.
Expand Down
10 changes: 5 additions & 5 deletions godot/src/Autoload/Delegates/StorageWorker.gd
Expand Up @@ -32,7 +32,7 @@ func _init(session: NakamaSession, client: NakamaClient, exception_handler: Exce
_exception_handler = exception_handler


# Async coroutine. Gets the list of characters belonging to the user out of
# Asynchronous coroutine. Gets the list of characters belonging to the user out of
# server storage.
# Returns an Array of {name: String, color: Color} dictionaries.
# Returns an empty array if there is a failure or if no characters are found.
Expand Down Expand Up @@ -117,7 +117,7 @@ func update_player_character_async(color: Color, name: String) -> int:
return OK


# Async coroutine. Delete the character at the specified index in the array from
# Asynchronous coroutine. Delete the character at the specified index in the array from
# player storage. Returns OK, a nakama error code, or ERR_PARAMETER_RANGE_ERROR
# if the index is too large or is invalid.
func delete_player_character_async(idx: int) -> int:
Expand All @@ -134,7 +134,7 @@ func delete_player_character_async(idx: int) -> int:
return ERR_PARAMETER_RANGE_ERROR


# Async coroutine. Get the last logged in character from the server, if any.
# Asynchronous coroutine. Get the last logged in character from the server, if any.
# Returns a {name: String, color: Color} dictionary, or an empty dictionary if no
# character is found, or something goes wrong.
func get_last_player_character_async() -> Dictionary:
Expand Down Expand Up @@ -165,7 +165,7 @@ func get_last_player_character_async() -> Dictionary:
return {}


# Async coroutine. Put the last logged in character into player storage on the server.
# Asynchronous coroutine. Put the last logged in character into player storage on the server.
# Returns OK, or a nakama error code.
func store_last_player_character_async(name: String, color: Color) -> int:
var character := {name = name, color = JSON.print(color)}
Expand All @@ -182,7 +182,7 @@ func store_last_player_character_async(name: String, color: Color) -> int:
return parsed_result


# Async coroutine. Writes the player's characters into storage on the server.
# Asynchronous coroutine. Writes the player's characters into storage on the server.
# Returns OK or a nakama error code.
func _write_player_characters_async(characters: Array) -> int:
var result: NakamaAPI.ApiStorageObjectAcks = yield(
Expand Down
108 changes: 108 additions & 0 deletions godot/src/Main/Game.gd
@@ -0,0 +1,108 @@
extends Node

const MAX_REQUEST_ATTEMPTS := 3
const LevelScene: PackedScene = preload("res://src/World/Level.tscn")

var level: Node

var _server_request_attempts := 0

onready var main_menu := $MainMenu
onready var character_menu := $CharacterMenu


func create_level(player_data: Dictionary) -> void:
level = LevelScene.instance()
add_child(level)
level.setup(player_data.name, player_data.color)


# Asks the server to create a new character asynchronously.
# Returns a dictionary with the player's name and color if it worked.
# Otherwise, returns an empty dictionary.
func create_character(name: String, color: Color) -> void:
var result: int = yield(Connection.create_player_character_async(color, name), "completed")

var data := {}
if result == ERR_UNAVAILABLE:
printerr("Character %s unavailable." % name)
elif result == OK:
data = {name = name, color = color}
Connection.store_last_player_character_async(name, color)
return data


# Gets a player's data asynchronously by `index` from the server.
func get_player(index: int) -> Dictionary:
var characters: Array = yield(Connection.get_player_characters_async(), "completed")
return characters[index]


# Attempts to connect to the server, then to join the world match.
func join_game_world() -> int:
var result: int = yield(Connection.connect_to_server_async(), "completed")

if result == OK:
result = yield(Connection.join_world_async(), "completed")

if result == OK:
emit_signal("server_request_succeeded")
else:
emit_signal(
"server_request_failed", "Error code %s: %s" % [result, Connection.error_message]
)
return result

# Requests the server to authenticate the player using their credentials.
# Attempts authentication up to `MAX_REQUEST_ATTEMPTS` times.
func authenticate_user(email: String, password: String, do_remember_email: bool) -> void:
var result := -1

while result != OK:
if _server_request_attempts < MAX_REQUEST_ATTEMPTS:
_server_request_attempts += 1
result = yield(_request_authentication(email, password, do_remember_email), "completed")
else:
break

if result == OK:
main_menu.hide()
character_menu.show()
else:
main_menu.update_status("Error code %s: %s" % [result, Connection.error_message])

_server_request_attempts = 0

# Requests the server to authenticate the player using their credentials.
func _request_authentication(email: String, password: String, do_remember_email := false) -> int:
main_menu.is_enabled = false

var result: int = yield(Connection.login_async(email, password), "completed")
if result == OK and do_remember_email:
Connection.save_email(email)

main_menu.is_enabled = true
return result


func _on_CharacterMenu_new_character_requested(name: String, color: Color) -> void:
create_character(name, color)


func _on_MainMenu_login_pressed(email: String, password: String, do_remember_email: bool) -> void:
authenticate_user(email, password, do_remember_email)


func _on_MainMenu_register_pressed(email: String, password: String, do_remember_email: bool) -> void:
main_menu.update_status("Authenticating...")
main_menu.is_enabled = false

var result: int = yield(
Connection.register_async(email, password), "completed"
)
if result == OK:
authenticate_user(email, password, do_remember_email)
else:
main_menu.update_status("Error code %s: %s" % [result, Connection.error_message])

main_menu.is_enabled = true
15 changes: 15 additions & 0 deletions godot/src/Main/Game.tscn
@@ -0,0 +1,15 @@
[gd_scene load_steps=4 format=2]

[ext_resource path="res://src/Main/Game.gd" type="Script" id=1]
[ext_resource path="res://src/Main/MainMenu.tscn" type="PackedScene" id=2]
[ext_resource path="res://src/UI/Menus/Characters/CharacterMenu.tscn" type="PackedScene" id=3]

[node name="Game" type="Node"]
script = ExtResource( 1 )

[node name="MainMenu" parent="." instance=ExtResource( 2 )]

[node name="CharacterMenu" parent="." instance=ExtResource( 3 )]
visible = false
[connection signal="login_pressed" from="MainMenu" to="." method="_on_MainMenu_login_pressed"]
[connection signal="register_pressed" from="MainMenu" to="." method="_on_MainMenu_register_pressed"]
42 changes: 31 additions & 11 deletions godot/src/Main/MainMenu.gd
@@ -1,33 +1,53 @@
# Aggregate class that holds the login, register, and character select controls
# and manages their visibility.
# The game's main menu. Aggregates the LoginForm and RegisterForm.
# Emits signals with relevant information for a parent node to communicate with the game server.
extends Control

export var CharacterSelect: PackedScene
signal login_pressed(email, password, do_remember_email)
signal register_pressed(email, password, do_remember_email)

var is_enabled := true setget set_is_enabled

onready var login_form := $LoginForm
onready var register_form := $RegisterForm

onready var menu_current: Control = login_form setget set_menu_current

func _ready() -> void:
self.menu_current = login_form


func set_is_enabled(value: bool) -> void:
is_enabled = value
for menu in get_children():
menu.is_enabled = is_enabled

func set_menu_current(value: Control) -> void:
menu_current = value
if not menu_current:
return

for child in get_children():
child.hide()
for menu in get_children():
menu.hide()
menu_current.show()


func _on_RegisterForm_opened() -> void:
login_form.hide()
register_form.show()
# Updates the status panel of the active menu.
func update_status(text: String) -> void:
menu_current.status_panel.text = text


func _on_RegisterForm_closed() -> void:
self.menu_current = login_form


func _on_LoginForm_register_pressed() -> void:
self.menu_current = register_form


func _on_LoginForm_login_pressed(email: String, password: String, do_remember_email: bool) -> void:
emit_signal("login_pressed", email, password, do_remember_email)


func _on_RegisterForm_register_pressed(email: String, password: String, do_remember_email: bool) -> void:
emit_signal("register_pressed", email, password, do_remember_email)


func _on_RegisterForm_cancel_pressed() -> void:
self.menu_current = login_form
8 changes: 4 additions & 4 deletions godot/src/Main/MainMenu.tscn
@@ -1,8 +1,7 @@
[gd_scene load_steps=5 format=2]
[gd_scene load_steps=4 format=2]

[ext_resource path="res://src/Main/MainMenu.gd" type="Script" id=1]
[ext_resource path="res://src/UI/Menus/Register/RegisterForm.tscn" type="PackedScene" id=2]
[ext_resource path="res://src/UI/Menus/Characters/CharacterMenu.tscn" type="PackedScene" id=3]
[ext_resource path="res://src/UI/Menus/Login/LoginForm.tscn" type="PackedScene" id=4]

[node name="MainMenu" type="Control"]
Expand All @@ -12,12 +11,13 @@ script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
CharacterSelect = ExtResource( 3 )

[node name="LoginForm" parent="." instance=ExtResource( 4 )]
visible = false

[node name="RegisterForm" parent="." instance=ExtResource( 2 )]
visible = false
[connection signal="login_pressed" from="LoginForm" to="." method="_on_LoginForm_login_pressed"]
[connection signal="register_pressed" from="LoginForm" to="." method="_on_LoginForm_register_pressed"]
[connection signal="closed" from="RegisterForm" to="." method="_on_RegisterForm_closed"]
[connection signal="cancel_pressed" from="RegisterForm" to="." method="_on_RegisterForm_cancel_pressed"]
[connection signal="register_pressed" from="RegisterForm" to="." method="_on_RegisterForm_register_pressed"]
5 changes: 4 additions & 1 deletion godot/src/UI/ChatBox.gd
Expand Up @@ -24,7 +24,10 @@ func add_text(text: String, sender_name: String, color: Color) -> void:
chat_log.bbcode_text = chat_log.bbcode_text.substr(chat_log.bbcode_text.find("\n"))
else:
reply_count += 1
chat_log.bbcode_text += "\n[color=#%s]%s[/color]: %s" % [color.to_html(false), sender_name, text]
chat_log.bbcode_text += (
"\n[color=#%s]%s[/color]: %s"
% [color.to_html(false), sender_name, text]
)


func send_chat_message() -> void:
Expand Down
8 changes: 5 additions & 3 deletions godot/src/UI/Elements/ColorSwatch.gd
Expand Up @@ -2,14 +2,16 @@ tool
extends Button
class_name ColorSwatch

onready var color_rect : ColorRect = $ColorRect
onready var color_rect: ColorRect = $ColorRect

export var color := Color('ffffff') setget set_color

export var color : = Color('ffffff') setget set_color

func _ready() -> void:
self.color = color

func set_color(value:Color) -> void:

func set_color(value: Color) -> void:
color = value
if not color_rect:
yield(self, "ready")
Expand Down

0 comments on commit 2976cf5

Please sign in to comment.