add silent wolf (:

This commit is contained in:
Harmony Honey 2023-11-26 21:58:12 -05:00
parent 6f9ce8356b
commit ee141c46c6
45 changed files with 4213 additions and 0 deletions

BIN
addons/silent_wolf/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,652 @@
extends Node
const CommonErrors = preload("res://addons/silent_wolf/common/CommonErrors.gd")
const SWLocalFileStorage = preload("res://addons/silent_wolf/utils/SWLocalFileStorage.gd")
const SWLogger = preload("res://addons/silent_wolf/utils/SWLogger.gd")
const UUID = preload("res://addons/silent_wolf/utils/UUID.gd")
signal sw_login_succeeded
signal sw_login_failed
signal sw_logout_succeeded
signal sw_registration_succeeded
signal sw_registration_user_pwd_succeeded
signal sw_registration_failed
signal sw_email_verif_succeeded
signal sw_email_verif_failed
signal sw_resend_conf_code_succeeded
signal sw_resend_conf_code_failed
signal sw_session_check_complete
signal sw_request_password_reset_succeeded
signal sw_request_password_reset_failed
signal sw_reset_password_succeeded
signal sw_reset_password_failed
signal sw_get_player_details_succeeded
signal sw_get_player_details_failed
var tmp_username = null
var logged_in_player = null
var logged_in_player_email = null
var logged_in_anon = false
var token = null
var id_token = null
var RegisterPlayer = null
var VerifyEmail = null
var ResendConfCode = null
var LoginPlayer = null
var ValidateSession = null
var RequestPasswordReset = null
var ResetPassword = null
var GetPlayerDetails = null
# wekrefs
var wrRegisterPlayer = null
var wrVerifyEmail = null
var wrResendConfCode = null
var wrLoginPlayer = null
var wrValidateSession = null
var wrRequestPasswordReset = null
var wrResetPassword = null
var wrGetPlayerDetails = null
var login_timeout = 0
var login_timer = null
var complete_session_check_wait_timer
func _ready():
pass
func set_player_logged_in(player_name):
logged_in_player = player_name
SWLogger.info("SilentWolf - player logged in as " + str(player_name))
if SilentWolf.auth_config.has("session_duration_seconds") and typeof(SilentWolf.auth_config.session_duration_seconds) == 2:
login_timeout = SilentWolf.auth_config.session_duration_seconds
else:
login_timeout = 0
SWLogger.info("SilentWolf login timeout: " + str(login_timeout))
if login_timeout != 0:
setup_login_timer()
func get_anon_user_id() -> String:
var anon_user_id = OS.get_unique_id()
if anon_user_id == '':
anon_user_id = UUID.generate_uuid_v4()
print("anon_user_id: " + str(anon_user_id))
return anon_user_id
func logout_player():
logged_in_player = null
# remove any player data if present
SilentWolf.Players.clear_player_data()
# remove stored session if any
remove_stored_session()
emit_signal("sw_logout_succeeded")
func register_player_anon(player_name=null) -> Node:
var user_local_id: String = get_anon_user_id()
RegisterPlayer = HTTPRequest.new()
wrRegisterPlayer = weakref(RegisterPlayer)
if OS.get_name() != "HTML5":
RegisterPlayer.set_use_threads(true)
get_tree().get_root().add_child(RegisterPlayer)
RegisterPlayer.connect("request_completed", self, "_on_RegisterPlayer_request_completed")
SWLogger.info("Calling SilentWolf to register an anonymous player")
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var api_key = SilentWolf.config.api_key
var payload = { "game_id": game_id, "anon": true, "player_name": player_name, "user_local_id": user_local_id }
var query = JSON.print(payload)
var headers = [
"Content-Type: application/json",
"x-api-key: " + api_key,
"x-sw-plugin-version: " + SilentWolf.version,
"x-sw-game-id: " + SilentWolf.config.game_id,
"x-sw-godot-version: " + SilentWolf.godot_version
]
#print("register_player headers: " + str(headers))
RegisterPlayer.request("https://api.silentwolf.com/create_new_player", headers, true, HTTPClient.METHOD_POST, query)
return self
func register_player(player_name, email, password, confirm_password):
tmp_username = player_name
RegisterPlayer = HTTPRequest.new()
wrRegisterPlayer = weakref(RegisterPlayer)
if OS.get_name() != "HTML5":
RegisterPlayer.set_use_threads(true)
get_tree().get_root().add_child(RegisterPlayer)
RegisterPlayer.connect("request_completed", self, "_on_RegisterPlayer_request_completed")
SWLogger.info("Calling SilentWolf to register a player")
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var api_key = SilentWolf.config.api_key
var payload = { "game_id": game_id, "anon": false, "player_name": player_name, "email": email, "password": password, "confirm_password": confirm_password }
var query = JSON.print(payload)
var headers = [
"Content-Type: application/json",
"x-api-key: " + api_key,
"x-sw-plugin-version: " + SilentWolf.version,
"x-sw-game-id: " + SilentWolf.config.game_id,
"x-sw-godot-version: " + SilentWolf.godot_version
]
#print("register_player headers: " + str(headers))
RegisterPlayer.request("https://api.silentwolf.com/create_new_player", headers, true, HTTPClient.METHOD_POST, query)
return self
func register_player_user_password(player_name, password, confirm_password):
tmp_username = player_name
RegisterPlayer = HTTPRequest.new()
wrRegisterPlayer = weakref(RegisterPlayer)
if OS.get_name() != "HTML5":
RegisterPlayer.set_use_threads(true)
get_tree().get_root().add_child(RegisterPlayer)
RegisterPlayer.connect("request_completed", self, "_on_RegisterPlayerUserPassword_request_completed")
SWLogger.info("Calling SilentWolf to register a player")
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var api_key = SilentWolf.config.api_key
var payload = { "game_id": game_id, "player_name": player_name, "password": password, "confirm_password": confirm_password }
var query = JSON.print(payload)
var headers = [
"Content-Type: application/json",
"x-api-key: " + api_key,
"x-sw-plugin-version: " + SilentWolf.version,
"x-sw-game-id: " + SilentWolf.config.game_id,
"x-sw-godot-version: " + SilentWolf.godot_version
]
#print("register_player headers: " + str(headers))
RegisterPlayer.request("https://api.silentwolf.com/create_new_player", headers, true, HTTPClient.METHOD_POST, query)
return self
func verify_email(player_name, code):
tmp_username = player_name
VerifyEmail = HTTPRequest.new()
wrVerifyEmail = weakref(VerifyEmail)
if OS.get_name() != "HTML5":
VerifyEmail.set_use_threads(true)
get_tree().get_root().add_child(VerifyEmail)
VerifyEmail.connect("request_completed", self, "_on_VerifyEmail_request_completed")
SWLogger.info("Calling SilentWolf to verify email address for: " + str(player_name))
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var api_key = SilentWolf.config.api_key
var payload = { "game_id": game_id, "username": player_name, "code": code }
var query = JSON.print(payload)
var headers = [
"Content-Type: application/json",
"x-api-key: " + api_key,
"x-sw-plugin-version: " + SilentWolf.version,
"x-sw-game-id: " + SilentWolf.config.game_id,
"x-sw-godot-version: " + SilentWolf.godot_version
]
#print("register_player headers: " + str(headers))
VerifyEmail.request("https://api.silentwolf.com/confirm_verif_code", headers, true, HTTPClient.METHOD_POST, query)
return self
func resend_conf_code(player_name):
ResendConfCode = HTTPRequest.new()
wrResendConfCode = weakref(ResendConfCode)
if OS.get_name() != "HTML5":
ResendConfCode.set_use_threads(true)
get_tree().get_root().add_child(ResendConfCode)
ResendConfCode.connect("request_completed", self, "_on_ResendConfCode_request_completed")
SWLogger.info("Calling SilentWolf to resend confirmation code for: " + str(player_name))
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var api_key = SilentWolf.config.api_key
var payload = { "game_id": game_id, "username": player_name }
var query = JSON.print(payload)
var headers = [
"Content-Type: application/json",
"x-api-key: " + api_key,
"x-sw-plugin-version: " + SilentWolf.version,
"x-sw-game-id: " + SilentWolf.config.game_id,
"x-sw-godot-version: " + SilentWolf.godot_version
]
#print("register_player headers: " + str(headers))
ResendConfCode.request("https://api.silentwolf.com/resend_conf_code", headers, true, HTTPClient.METHOD_POST, query)
return self
func login_player(username, password, remember_me=false):
tmp_username = username
LoginPlayer = HTTPRequest.new()
wrLoginPlayer = weakref(LoginPlayer)
print("OS name: " + str(OS.get_name()))
if OS.get_name() != "HTML5":
LoginPlayer.set_use_threads(true)
print("get_tree().get_root(): " + str(get_tree().get_root()))
get_tree().get_root().add_child(LoginPlayer)
LoginPlayer.connect("request_completed", self, "_on_LoginPlayer_request_completed")
SWLogger.info("Calling SilentWolf to log in a player")
var game_id = SilentWolf.config.game_id
var api_key = SilentWolf.config.api_key
var payload = { "game_id": game_id, "username": username, "password": password, "remember_me": str(remember_me) }
if SilentWolf.auth_config.has("saved_session_expiration_days") and typeof(SilentWolf.auth_config.saved_session_expiration_days) == 2:
payload["remember_me_expires_in"] = str(SilentWolf.auth_config.saved_session_expiration_days)
SWLogger.debug("SilentWolf login player payload: " + str(payload))
var query = JSON.print(payload)
var headers = [
"Content-Type: application/json",
"x-api-key: " + api_key,
"x-sw-plugin-version: " + SilentWolf.version,
"x-sw-game-id: " + SilentWolf.config.game_id,
"x-sw-godot-version: " + SilentWolf.godot_version
]#print("login_player headers: " + str(headers))
LoginPlayer.request("https://api.silentwolf.com/login_player", headers, true, HTTPClient.METHOD_POST, query)
return self
func request_player_password_reset(player_name):
RequestPasswordReset = HTTPRequest.new()
wrRequestPasswordReset = weakref(RequestPasswordReset)
print("OS name: " + str(OS.get_name()))
if OS.get_name() != "HTML5":
RequestPasswordReset.set_use_threads(true)
get_tree().get_root().add_child(RequestPasswordReset)
RequestPasswordReset.connect("request_completed", self, "_on_RequestPasswordReset_request_completed")
SWLogger.info("Calling SilentWolf to request a password reset for: " + str(player_name))
var game_id = SilentWolf.config.game_id
var api_key = SilentWolf.config.api_key
var payload = { "game_id": game_id, "player_name": player_name }
SWLogger.debug("SilentWolf request player password reset payload: " + str(payload))
var query = JSON.print(payload)
var headers = [
"Content-Type: application/json",
"x-api-key: " + api_key,
"x-sw-plugin-version: " + SilentWolf.version,
"x-sw-game-id: " + SilentWolf.config.game_id,
"x-sw-godot-version: " + SilentWolf.godot_version
]
RequestPasswordReset.request("https://api.silentwolf.com/request_player_password_reset", headers, true, HTTPClient.METHOD_POST, query)
return self
func reset_player_password(player_name, conf_code, new_password, confirm_password):
ResetPassword = HTTPRequest.new()
wrResetPassword = weakref(ResetPassword)
if OS.get_name() != "HTML5":
ResetPassword.set_use_threads(true)
get_tree().get_root().add_child(ResetPassword)
ResetPassword.connect("request_completed", self, "_on_ResetPassword_request_completed")
SWLogger.info("Calling SilentWolf to reset password for: " + str(player_name))
var game_id = SilentWolf.config.game_id
var api_key = SilentWolf.config.api_key
var payload = { "game_id": game_id, "player_name": player_name, "conf_code": conf_code, "password": new_password, "confirm_password": confirm_password }
SWLogger.debug("SilentWolf request player password reset payload: " + str(payload))
var query = JSON.print(payload)
var headers = [
"Content-Type: application/json",
"x-api-key: " + api_key,
"x-sw-plugin-version: " + SilentWolf.version,
"x-sw-game-id: " + SilentWolf.config.game_id,
"x-sw-godot-version: " + SilentWolf.godot_version
]
ResetPassword.request("https://api.silentwolf.com/reset_player_password", headers, true, HTTPClient.METHOD_POST, query)
return self
func get_player_details(player_name):
GetPlayerDetails = HTTPRequest.new()
wrRegisterPlayer = weakref(GetPlayerDetails)
if OS.get_name() != "HTML5":
GetPlayerDetails.set_use_threads(true)
get_tree().get_root().add_child(GetPlayerDetails)
GetPlayerDetails.connect("request_completed", self, "_on_GetPlayerDetails_request_completed")
SWLogger.info("Calling SilentWolf to get player details")
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var api_key = SilentWolf.config.api_key
var payload = { "game_id": game_id, "player_name": player_name }
var query = JSON.print(payload)
var headers = [
"Content-Type: application/json",
"x-api-key: " + api_key,
"x-sw-plugin-version: " + SilentWolf.version,
"x-sw-game-id: " + SilentWolf.config.game_id,
"x-sw-godot-version: " + SilentWolf.godot_version
]
#print("register_player headers: " + str(headers))
GetPlayerDetails.request("https://api.silentwolf.com/get_player_details", headers, true, HTTPClient.METHOD_POST, query)
return self
func _on_LoginPlayer_request_completed( result, response_code, headers, body ):
SWLogger.info("LoginPlayer request completed")
var status_check = CommonErrors.check_status_code(response_code)
#LoginPlayer.queue_free()
SilentWolf.free_request(wrLoginPlayer, LoginPlayer)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
#SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/leaderboard")
else:
if "lookup" in response.keys():
print("remember me lookup: " + str(response.lookup))
save_session(response.lookup, response.validator)
if "validator" in response.keys():
print("remember me validator: " + str(response.validator))
SWLogger.info("SilentWolf login player success? : " + str(response.success))
# TODO: get JWT token and store it
# send a different signal depending on login success or failure
if response.success:
token = response.swtoken
#id_token = response.swidtoken
SWLogger.debug("token: " + token)
set_player_logged_in(tmp_username)
emit_signal("sw_login_succeeded")
else:
emit_signal("sw_login_failed", response.error)
func _on_RegisterPlayer_request_completed( result, response_code, headers, body ):
SWLogger.info("RegisterPlayer request completed")
var status_check = CommonErrors.check_status_code(response_code)
#RegisterPlayer.queue_free()
SilentWolf.free_request(wrRegisterPlayer, RegisterPlayer)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
SWLogger.debug("reponse: " + str(response))
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/leaderboard")
else:
SWLogger.info("SilentWolf create new player success? : " + str(response.success))
# also get a JWT token here
# send a different signal depending on registration success or failure
if response.success:
var anon = response.anon
if anon:
SWLogger.info("Anonymous Player registration succeeded")
logged_in_anon = true
if 'player_name' in response:
logged_in_player = response.player_name
elif 'player_local_id' in response:
logged_in_player = str("anon##" + response.player_local_id)
else:
logged_in_player = "anon##unknown"
print("Anon registration, logged in player: " + str(logged_in_player))
else:
# if email confirmation is enabled for the game, we can't log in the player just yet
var email_conf_enabled = response.email_conf_enabled
if email_conf_enabled:
SWLogger.info("Player registration succeeded, but player still needs to verify email address")
else:
SWLogger.info("Player registration succeeded, email verification is disabled")
logged_in_player = tmp_username
emit_signal("sw_registration_succeeded")
else:
emit_signal("sw_registration_failed", response.error)
func _on_RegisterPlayerUserPassword_request_completed( result, response_code, headers, body ):
SWLogger.info("RegisterPlayerUserPassword request completed")
var status_check = CommonErrors.check_status_code(response_code)
#RegisterPlayer.queue_free()
SilentWolf.free_request(wrRegisterPlayer, RegisterPlayer)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
SWLogger.debug("reponse: " + str(response))
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/leaderboard")
else:
SWLogger.info("SilentWolf create new player success? : " + str(response.success))
# also get a JWT token here
# send a different signal depending on registration success or failure
if response.success:
# if email confirmation is enabled for the game, we can't log in the player just yet
var email_conf_enabled = response.email_conf_enabled
SWLogger.info("Player registration with username/password succeeded, player account autoconfirmed")
logged_in_player = tmp_username
emit_signal("sw_registration_user_pwd_succeeded")
else:
emit_signal("sw_registration_failed", response.error)
func _on_VerifyEmail_request_completed( result, response_code, headers, body ):
SWLogger.info("VerifyEmail request completed")
var status_check = CommonErrors.check_status_code(response_code)
SilentWolf.free_request(wrVerifyEmail, VerifyEmail)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
SWLogger.debug("reponse: " + str(response))
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/playerauth")
else:
SWLogger.info("SilentWolf verify email success? : " + str(response.success))
# also get a JWT token here
# send a different signal depending on registration success or failure
if response.success:
logged_in_player = tmp_username
emit_signal("sw_email_verif_succeeded")
else:
emit_signal("sw_email_verif_failed", response.error)
func _on_ResendConfCode_request_completed( result, response_code, headers, body ):
SWLogger.info("ResendConfCode request completed")
var status_check = CommonErrors.check_status_code(response_code)
SilentWolf.free_request(wrResendConfCode, ResendConfCode)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
SWLogger.debug("reponse: " + str(response))
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/playerauth")
else:
SWLogger.info("SilentWolf resend conf code success? : " + str(response.success))
# also get a JWT token here
# send a different signal depending on registration success or failure
if response.success:
emit_signal("sw_resend_conf_code_succeeded")
else:
emit_signal("sw_resend_conf_code_failed", response.error)
func _on_RequestPasswordReset_request_completed( result, response_code, headers, body ):
SWLogger.info("RequestPasswordReset request completed")
var status_check = CommonErrors.check_status_code(response_code)
SilentWolf.free_request(wrRequestPasswordReset, RequestPasswordReset)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
SWLogger.debug("reponse: " + str(response))
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/playerauth")
else:
SWLogger.info("SilentWolf request player password reset success? : " + str(response.success))
if response.success:
emit_signal("sw_request_password_reset_succeeded")
else:
emit_signal("sw_request_password_reset_failed", response.error)
func _on_ResetPassword_request_completed( result, response_code, headers, body ):
SWLogger.info("ResetPassword request completed")
var status_check = CommonErrors.check_status_code(response_code)
SilentWolf.free_request(wrResetPassword, ResetPassword)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
SWLogger.debug("reponse: " + str(response))
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/playerauth")
else:
SWLogger.info("SilentWolf reset player password success? : " + str(response.success))
if response.success:
emit_signal("sw_reset_password_succeeded")
else:
emit_signal("sw_reset_password_failed", response.error)
func setup_login_timer():
login_timer = Timer.new()
login_timer.set_one_shot(true)
login_timer.set_wait_time(login_timeout)
login_timer.connect("timeout", self, "on_login_timeout_complete")
add_child(login_timer)
func on_login_timeout_complete():
logout_player()
# store lookup (not logged in player name) and validator in local file
func save_session(lookup, validator):
var path = "user://swsession.save"
var session_data = {
"lookup": lookup,
"validator": validator
}
SWLocalFileStorage.save_data("user://swsession.save", session_data, "Saving SilentWolf session: ")
func remove_stored_session():
var path = "user://swsession.save"
SWLocalFileStorage.remove_data(path, "Removing SilentWolf session if any: " )
# reload lookup and validator and send them back to the server to auto-login user
func load_session() -> Dictionary:
var sw_session_data = null
var path = "user://swsession.save"
sw_session_data = SWLocalFileStorage.get_data(path)
if sw_session_data == null:
SWLogger.debug("No local SilentWolf session stored, or session data stored in incorrect format")
SWLogger.info("Found session data: " + str(sw_session_data))
return sw_session_data
func auto_login_player():
var sw_session_data = load_session()
if sw_session_data:
SWLogger.debug("Found saved SilentWolf session data, attempting autologin...")
var lookup = sw_session_data.lookup
var validator = sw_session_data.validator
# whether successful or not, in the end the "sw_session_check_complete" signal will be emitted
validate_player_session(lookup, validator)
else:
SWLogger.debug("No saved SilentWolf session data, so no autologin will be performed")
# the following is needed to delay the emission of the signal just a little bit, otherwise the signal is never received!
setup_complete_session_check_wait_timer()
complete_session_check_wait_timer.start()
return self
# Signal can't be emitted directly from auto_login_player() function
# otherwise it won't connect back to calling script
func complete_session_check(return_value=null):
SWLogger.debug("emitting signal....")
emit_signal("sw_session_check_complete", return_value)
func validate_player_session(lookup, validator, scene=get_tree().get_current_scene()):
ValidateSession = HTTPRequest.new()
wrValidateSession = weakref(ValidateSession)
if OS.get_name() != "HTML5":
ValidateSession.set_use_threads(true)
scene.add_child(ValidateSession)
ValidateSession.connect("request_completed", self, "_on_ValidateSession_request_completed")
SWLogger.info("Calling SilentWolf to validate an existing player session")
var game_id = SilentWolf.config.game_id
var api_key = SilentWolf.config.api_key
var payload = { "game_id": game_id, "lookup": lookup, "validator": validator }
SWLogger.debug("Validate session payload: " + str(payload))
var query = JSON.print(payload)
var headers = ["Content-Type: application/json", "x-api-key: " + api_key, "x-sw-plugin-version: " + SilentWolf.version]
ValidateSession.request("https://api.silentwolf.com/validate_remember_me", headers, true, HTTPClient.METHOD_POST, query)
return self
func _on_ValidateSession_request_completed( result, response_code, headers, body ):
SWLogger.info("SilentWolf - ValidateSession request completed")
var status_check = CommonErrors.check_status_code(response_code)
#ValidateSession.queue_free()
SilentWolf.free_request(wrValidateSession, ValidateSession)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
SWLogger.debug("reponse: " + str(response))
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/leaderboard")
else:
SWLogger.info("SilentWolf validate session success? : " + str(response.success))
if response.success:
set_player_logged_in(response.player_name)
complete_session_check(logged_in_player)
else:
complete_session_check(response.error)
func _on_GetPlayerDetails_request_completed( result, response_code, headers, body ):
SWLogger.info("SilentWolf - GetPlayerDetails request completed")
var status_check = CommonErrors.check_status_code(response_code)
#ValidateSession.queue_free()
SilentWolf.free_request(wrGetPlayerDetails, GetPlayerDetails)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
SWLogger.debug("reponse: " + str(response))
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/playerauth")
else:
SWLogger.info("SilentWolf get player details success? : " + str(response.success))
if response.success:
emit_signal("sw_get_player_details_succeeded", response.player_details)
else:
emit_signal("sw_get_player_details_failed")
func setup_complete_session_check_wait_timer():
complete_session_check_wait_timer = Timer.new()
complete_session_check_wait_timer.set_one_shot(true)
complete_session_check_wait_timer.set_wait_time(0.01)
complete_session_check_wait_timer.connect("timeout", self, "complete_session_check")
add_child(complete_session_check_wait_timer)

View file

@ -0,0 +1,60 @@
extends TextureRect
const SWLogger = preload("res://addons/silent_wolf/utils/SWLogger.gd")
func _ready():
SilentWolf.Auth.connect("sw_email_verif_succeeded", self, "_on_confirmation_succeeded")
SilentWolf.Auth.connect("sw_email_verif_failed", self, "_on_confirmation_failed")
SilentWolf.Auth.connect("sw_resend_conf_code_succeeded", self, "_on_resend_code_succeeded")
SilentWolf.Auth.connect("sw_resend_conf_code_failed", self, "_on_resend_code_failed")
func _on_confirmation_succeeded():
SWLogger.info("email verification succeeded: " + str(SilentWolf.Auth.logged_in_player))
# redirect to configured scene (user is logged in after registration)
var scene_name = SilentWolf.auth_config.redirect_to_scene
get_tree().change_scene(scene_name)
func _on_confirmation_failed(error):
hide_processing_label()
SWLogger.info("email verification failed: " + str(error))
$"FormContainer/ErrorMessage".text = error
$"FormContainer/ErrorMessage".show()
func _on_resend_code_succeeded():
SWLogger.info("Code resend succeeded for player: " + str(SilentWolf.Auth.tmp_username))
$"FormContainer/ErrorMessage".text = "Confirmation code was resent to your email address. Please check your inbox (and your spam)."
$"FormContainer/ErrorMessage".show()
func _on_resend_code_failed():
SWLogger.info("Code resend failed for player: " + str(SilentWolf.Auth.tmp_username))
$"FormContainer/ErrorMessage".text = "Confirmation code could not be resent"
$"FormContainer/ErrorMessage".show()
func show_processing_label():
$"FormContainer/ProcessingLabel".show()
func hide_processing_label():
$"FormContainer/ProcessingLabel".hide()
func _on_ConfirmButton_pressed():
var username = SilentWolf.Auth.tmp_username
var code = $"FormContainer/CodeContainer/VerifCode".text
SWLogger.debug("Email verification form submitted, code: " + str(code))
SilentWolf.Auth.verify_email(username, code)
show_processing_label()
func _on_ResendConfCodeButton_pressed():
var username = SilentWolf.Auth.tmp_username
SWLogger.debug("Requesting confirmation code resend")
SilentWolf.Auth.resend_conf_code(username)
show_processing_label()

View file

@ -0,0 +1,143 @@
[gd_scene load_steps=13 format=2]
[ext_resource path="res://addons/silent_wolf/Auth/ConfirmEmail.gd" type="Script" id=1]
[ext_resource path="res://addons/silent_wolf/common/SWButton.tscn" type="PackedScene" id=2]
[ext_resource path="res://addons/silent_wolf/assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=3]
[sub_resource type="DynamicFontData" id=13]
font_path = "res://addons/silent_wolf/assets/fonts/Comfortaa-Bold.ttf"
[sub_resource type="DynamicFont" id=1]
size = 64
font_data = SubResource( 13 )
[sub_resource type="DynamicFont" id=2]
size = 48
font_data = SubResource( 13 )
[sub_resource type="DynamicFont" id=3]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=4]
size = 32
font_data = SubResource( 13 )
[sub_resource type="StyleBoxFlat" id=5]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=6]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=7]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.482353, 0.458824, 0.458824, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="DynamicFont" id=8]
size = 64
font_data = ExtResource( 3 )
[node name="ConfirmEmail" type="TextureRect"]
margin_right = 40.0
margin_bottom = 40.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="FormContainer" type="VBoxContainer" parent="."]
margin_left = 565.0
margin_top = 197.0
margin_right = 1500.0
margin_bottom = 798.0
custom_constants/separation = 100
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Label" type="Label" parent="FormContainer"]
margin_right = 935.0
margin_bottom = 72.0
custom_fonts/font = SubResource( 1 )
text = "Confirm your email address"
align = 1
[node name="CodeContainer" type="HBoxContainer" parent="FormContainer"]
margin_top = 172.0
margin_right = 935.0
margin_bottom = 247.0
custom_constants/separation = 20
[node name="Label" type="Label" parent="FormContainer/CodeContainer"]
margin_top = 10.0
margin_right = 133.0
margin_bottom = 65.0
custom_fonts/font = SubResource( 2 )
text = "Code"
[node name="VerifCode" type="LineEdit" parent="FormContainer/CodeContainer"]
margin_left = 153.0
margin_right = 483.0
margin_bottom = 75.0
rect_min_size = Vector2( 330, 75 )
custom_fonts/font = SubResource( 3 )
max_length = 30
[node name="ErrorMessage" type="Label" parent="FormContainer"]
visible = false
margin_top = 522.0
margin_right = 648.0
margin_bottom = 559.0
custom_fonts/font = SubResource( 4 )
custom_colors/font_color = Color( 0.866667, 0.101961, 0.101961, 1 )
autowrap = true
[node name="ConfirmButton" parent="FormContainer" instance=ExtResource( 2 )]
margin_top = 347.0
margin_right = 935.0
margin_bottom = 465.0
text = "Submit"
[node name="ResendConfCodeButton" parent="FormContainer" instance=ExtResource( 2 )]
margin_top = 565.0
margin_right = 935.0
margin_bottom = 683.0
custom_styles/hover = SubResource( 5 )
custom_styles/pressed = SubResource( 6 )
custom_styles/normal = SubResource( 7 )
custom_fonts/font = SubResource( 8 )
text = "Resend code"
[node name="ProcessingLabel" type="Label" parent="FormContainer"]
visible = false
margin_top = 740.0
margin_right = 648.0
margin_bottom = 812.0
custom_fonts/font = SubResource( 1 )
text = "Processing..."
align = 1
[connection signal="pressed" from="FormContainer/ConfirmButton" to="." method="_on_ConfirmButton_pressed"]
[connection signal="pressed" from="FormContainer/ResendConfCodeButton" to="." method="_on_ResendConfCodeButton_pressed"]

View file

@ -0,0 +1,41 @@
extends TextureRect
const SWLogger = preload("res://addons/silent_wolf/utils/SWLogger.gd")
func _ready():
#var auth_node = get_tree().get_root().get_node("res://addons/silent_wolf/Auth/Auth")
SilentWolf.Auth.connect("sw_login_succeeded", self, "_on_login_succeeded")
SilentWolf.Auth.connect("sw_login_failed", self, "_on_login_failed")
func _on_LoginButton_pressed():
var username = $"FormContainer/UsernameContainer/Username".text
var password = $"FormContainer/PasswordContainer/Password".text
var remember_me = $"FormContainer/RememberMeCheckBox".is_pressed()
SWLogger.debug("Login form submitted, remember_me: " + str(remember_me))
SilentWolf.Auth.login_player(username, password, remember_me)
show_processing_label()
func _on_login_succeeded():
var scene_name = SilentWolf.auth_config.redirect_to_scene
SWLogger.info("logged in as: " + str(SilentWolf.Auth.logged_in_player))
get_tree().change_scene(scene_name)
func _on_login_failed(error):
hide_processing_label()
SWLogger.info("log in failed: " + str(error))
$"FormContainer/ErrorMessage".text = error
$"FormContainer/ErrorMessage".show()
func _on_BackButton_pressed():
get_tree().change_scene(SilentWolf.auth_config.redirect_to_scene)
func show_processing_label():
$"FormContainer/ProcessingLabel".show()
$"FormContainer/ProcessingLabel".show()
func hide_processing_label():
$"FormContainer/ProcessingLabel".hide()
func _on_LinkButton_pressed():
get_tree().change_scene(SilentWolf.auth_config.reset_password_scene)

View file

@ -0,0 +1,208 @@
[gd_scene load_steps=19 format=2]
[ext_resource path="res://addons/silent_wolf/Auth/Login.gd" type="Script" id=1]
[ext_resource path="res://addons/silent_wolf/common/SWButton.tscn" type="PackedScene" id=2]
[ext_resource path="res://addons/silent_wolf/assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=3]
[ext_resource path="res://addons/silent_wolf/assets/gfx/checkbox_unchecked.png" type="Texture" id=5]
[ext_resource path="res://addons/silent_wolf/assets/gfx/checkbox_checked.png" type="Texture" id=6]
[sub_resource type="StyleBoxFlat" id=1]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=2]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=3]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.482353, 0.458824, 0.458824, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="DynamicFont" id=4]
size = 64
font_data = ExtResource( 3 )
[sub_resource type="DynamicFontData" id=13]
font_path = "res://addons/silent_wolf/assets/fonts/Comfortaa-Bold.ttf"
[sub_resource type="DynamicFont" id=5]
size = 64
font_data = SubResource( 13 )
[sub_resource type="DynamicFont" id=6]
size = 48
font_data = SubResource( 13 )
[sub_resource type="DynamicFont" id=7]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=8]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=9]
size = 32
outline_color = Color( 0.211765, 0.25098, 0.937255, 1 )
font_data = SubResource( 13 )
[sub_resource type="StyleBoxFlat" id=10]
content_margin_left = 5.0
bg_color = Color( 0.6, 0.6, 0.6, 0 )
[sub_resource type="DynamicFont" id=11]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=12]
size = 32
font_data = SubResource( 13 )
[node name="Login" type="TextureRect"]
margin_right = 40.0
margin_bottom = 40.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="BackButton" parent="." instance=ExtResource( 2 )]
margin_left = 339.0
margin_top = 117.0
margin_right = 667.0
margin_bottom = 235.0
custom_styles/hover = SubResource( 1 )
custom_styles/pressed = SubResource( 2 )
custom_styles/normal = SubResource( 3 )
custom_fonts/font = SubResource( 4 )
text = "← Back"
[node name="FormContainer" type="VBoxContainer" parent="."]
margin_left = 679.0
margin_top = 196.75
margin_right = 1327.0
margin_bottom = 933.75
custom_constants/separation = 80
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Label" type="Label" parent="FormContainer"]
margin_right = 648.0
margin_bottom = 72.0
custom_fonts/font = SubResource( 5 )
text = "Log in"
align = 1
[node name="UsernameContainer" type="HBoxContainer" parent="FormContainer"]
margin_top = 152.0
margin_right = 648.0
margin_bottom = 227.0
custom_constants/separation = 20
[node name="Label" type="Label" parent="FormContainer/UsernameContainer"]
margin_top = 10.0
margin_right = 269.0
margin_bottom = 65.0
custom_fonts/font = SubResource( 6 )
text = "Username:"
[node name="Username" type="LineEdit" parent="FormContainer/UsernameContainer"]
margin_left = 289.0
margin_right = 619.0
margin_bottom = 75.0
rect_min_size = Vector2( 330, 75 )
custom_fonts/font = SubResource( 7 )
max_length = 30
[node name="PasswordContainer" type="HBoxContainer" parent="FormContainer"]
margin_top = 307.0
margin_right = 648.0
margin_bottom = 382.0
custom_constants/separation = 40
[node name="Label" type="Label" parent="FormContainer/PasswordContainer"]
margin_top = 10.0
margin_right = 247.0
margin_bottom = 65.0
custom_fonts/font = SubResource( 6 )
text = "Password:"
[node name="Password" type="LineEdit" parent="FormContainer/PasswordContainer"]
margin_left = 287.0
margin_right = 617.0
margin_bottom = 75.0
rect_min_size = Vector2( 330, 75 )
custom_fonts/font = SubResource( 8 )
max_length = 30
secret = true
[node name="LinkButton" type="LinkButton" parent="FormContainer"]
margin_top = 462.0
margin_right = 648.0
margin_bottom = 499.0
custom_fonts/font = SubResource( 9 )
custom_colors/font_color = Color( 0.321569, 0.360784, 0.92549, 1 )
text = "Forgot Password?"
[node name="RememberMeCheckBox" type="CheckBox" parent="FormContainer"]
margin_top = 579.0
margin_right = 648.0
margin_bottom = 629.0
rect_min_size = Vector2( 50, 50 )
focus_mode = 0
custom_icons/checked = ExtResource( 6 )
custom_icons/unchecked = ExtResource( 5 )
custom_styles/normal = SubResource( 10 )
custom_fonts/font = SubResource( 11 )
custom_constants/hseparation = 15
text = "Stay signed in for 30 days"
expand_icon = true
[node name="ErrorMessage" type="Label" parent="FormContainer"]
visible = false
margin_top = 522.0
margin_right = 648.0
margin_bottom = 559.0
custom_fonts/font = SubResource( 12 )
custom_colors/font_color = Color( 0.866667, 0.101961, 0.101961, 1 )
autowrap = true
[node name="LoginButton" parent="FormContainer" instance=ExtResource( 2 )]
margin_top = 709.0
margin_right = 648.0
margin_bottom = 827.0
text = "Submit"
[node name="ProcessingLabel" type="Label" parent="FormContainer"]
visible = false
margin_top = 740.0
margin_right = 648.0
margin_bottom = 812.0
custom_fonts/font = SubResource( 5 )
text = "Processing..."
align = 1
[connection signal="pressed" from="BackButton" to="." method="_on_BackButton_pressed"]
[connection signal="pressed" from="FormContainer/LinkButton" to="." method="_on_LinkButton_pressed"]
[connection signal="pressed" from="FormContainer/LoginButton" to="." method="_on_LoginButton_pressed"]

View file

@ -0,0 +1,81 @@
extends TextureRect
const SWLogger = preload("res://addons/silent_wolf/utils/SWLogger.gd")
func _ready():
SilentWolf.check_auth_ready()
SilentWolf.Auth.connect("sw_registration_succeeded", self, "_on_registration_succeeded")
SilentWolf.Auth.connect("sw_registration_user_pwd_succeeded", self, "_on_registration_succeeded")
SilentWolf.Auth.connect("sw_registration_failed", self, "_on_registration_failed")
func _on_RegisterButton_pressed():
var player_name = $"FormContainer/MainFormContainer/FormInputFields/PlayerName".text
var email = $"FormContainer/MainFormContainer/FormInputFields/Email".text
var password = $"FormContainer/MainFormContainer/FormInputFields/Password".text
var confirm_password = $"FormContainer/MainFormContainer/FormInputFields/ConfirmPassword".text
SilentWolf.Auth.register_player(player_name, email, password, confirm_password)
show_processing_label()
func _on_RegisterUPButton_pressed():
var player_name = $"FormContainer/MainFormContainer/FormInputFields/PlayerName".text
var password = $"FormContainer/MainFormContainer/FormInputFields/Password".text
var confirm_password = $"FormContainer/MainFormContainer/FormInputFields/ConfirmPassword".text
SilentWolf.Auth.register_player_user_password(player_name, password, confirm_password)
show_processing_label()
func _on_registration_succeeded():
#get_tree().change_scene("res://addons/silent_wolf/Auth/Login.tscn")
# redirect to configured scene (user is logged in after registration)
var scene_name = SilentWolf.auth_config.redirect_to_scene
# if doing email verification, open scene to confirm email address
if ("email_confirmation_scene" in SilentWolf.auth_config) and (SilentWolf.auth_config.email_confirmation_scene) != "":
SWLogger.info("registration succeeded, waiting for email verification...")
scene_name = SilentWolf.auth_config.email_confirmation_scene
else:
SWLogger.info("registration succeeded, logged in player: " + str(SilentWolf.Auth.logged_in_player))
get_tree().change_scene(scene_name)
func _on_registration_user_pwd_succeeded():
var scene_name = SilentWolf.auth_config.redirect_to_scene
get_tree().change_scene(scene_name)
func _on_registration_failed(error):
hide_processing_label()
SWLogger.info("registration failed: " + str(error))
$"FormContainer/ErrorMessage".text = error
$"FormContainer/ErrorMessage".show()
func _on_BackButton_pressed():
get_tree().change_scene(SilentWolf.auth_config.redirect_to_scene)
func show_processing_label():
$"FormContainer/ProcessingLabel".show()
func hide_processing_label():
$"FormContainer/ProcessingLabel".hide()
func _on_UsernameToolButton_mouse_entered():
$"FormContainer/InfoBox".text = "Username should contain at least 6 characters (letters or numbers) and no spaces."
$"FormContainer/InfoBox".show()
func _on_UsernameToolButton_mouse_exited():
$"FormContainer/InfoBox".hide()
func _on_PasswordToolButton_mouse_entered():
$"FormContainer/InfoBox".text = "Password should contain at least 8 characters including uppercase and lowercase letters, numbers and (optionally) special characters."
$"FormContainer/InfoBox".show()
func _on_PasswordToolButton_mouse_exited():
$"FormContainer/InfoBox".hide()

View file

@ -0,0 +1,285 @@
[gd_scene load_steps=21 format=2]
[ext_resource path="res://addons/silent_wolf/Auth/Register.gd" type="Script" id=1]
[ext_resource path="res://addons/silent_wolf/common/SWButton.tscn" type="PackedScene" id=2]
[ext_resource path="res://addons/silent_wolf/assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=3]
[ext_resource path="res://assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=4]
[ext_resource path="res://addons/silent_wolf/assets/gfx/info_icon_small.png" type="Texture" id=5]
[ext_resource path="res://addons/silent_wolf/assets/gfx/dummy_info_icon_small.png" type="Texture" id=6]
[sub_resource type="StyleBoxFlat" id=1]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=2]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=3]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.482353, 0.458824, 0.458824, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="DynamicFont" id=4]
size = 64
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=5]
size = 64
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=6]
size = 64
font_data = ExtResource( 4 )
[sub_resource type="DynamicFont" id=7]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=8]
size = 32
font_data = ExtResource( 4 )
[sub_resource type="DynamicFont" id=9]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=10]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="StyleBoxFlat" id=11]
content_margin_left = 30.0
content_margin_right = 30.0
content_margin_top = 30.0
content_margin_bottom = 30.0
bg_color = Color( 1, 1, 1, 1 )
border_width_left = 3
border_width_top = 3
border_width_right = 3
border_width_bottom = 3
border_color = Color( 0.666667, 0.223529, 0.223529, 1 )
corner_radius_top_left = 10
corner_radius_top_right = 10
corner_radius_bottom_right = 10
corner_radius_bottom_left = 10
[sub_resource type="DynamicFont" id=12]
size = 32
outline_color = Color( 0.666667, 0.223529, 0.223529, 1 )
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=13]
size = 32
outline_color = Color( 0.854902, 0.0901961, 0.0901961, 1 )
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=14]
size = 32
font_data = ExtResource( 3 )
[node name="Register" type="TextureRect"]
margin_right = 40.0
margin_bottom = 40.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="BackButton" parent="." instance=ExtResource( 2 )]
margin_left = 241.0
margin_top = 54.0
margin_right = 529.0
margin_bottom = 172.0
custom_styles/hover = SubResource( 1 )
custom_styles/pressed = SubResource( 2 )
custom_styles/normal = SubResource( 3 )
custom_fonts/font = SubResource( 4 )
text = "← Back"
[node name="FormContainer" type="VBoxContainer" parent="."]
margin_left = 396.0
margin_top = 200.0
margin_right = 1665.0
margin_bottom = 1060.0
grow_horizontal = 0
grow_vertical = 0
custom_constants/separation = 60
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Label" type="Label" parent="FormContainer"]
margin_right = 1269.0
margin_bottom = 72.0
custom_fonts/font = SubResource( 5 )
text = "Sign up"
align = 1
[node name="MainFormContainer" type="HBoxContainer" parent="FormContainer"]
margin_top = 132.0
margin_right = 1269.0
margin_bottom = 525.0
custom_constants/separation = 30
[node name="FormLabels" type="VBoxContainer" parent="FormContainer/MainFormContainer"]
margin_right = 625.0
margin_bottom = 393.0
custom_constants/separation = 30
[node name="PlayerNameLabel" type="Label" parent="FormContainer/MainFormContainer/FormLabels"]
margin_right = 625.0
margin_bottom = 80.0
rect_min_size = Vector2( 0, 80 )
custom_fonts/font = SubResource( 6 )
text = "Username:"
[node name="EmailLabel" type="Label" parent="FormContainer/MainFormContainer/FormLabels"]
margin_top = 110.0
margin_right = 625.0
margin_bottom = 182.0
custom_fonts/font = SubResource( 6 )
text = "Email:"
[node name="PasswordLabel" type="Label" parent="FormContainer/MainFormContainer/FormLabels"]
margin_top = 212.0
margin_right = 625.0
margin_bottom = 284.0
custom_fonts/font = SubResource( 6 )
text = "Password:"
[node name="ConfirmPasswordLabel" type="Label" parent="FormContainer/MainFormContainer/FormLabels"]
margin_top = 314.0
margin_right = 625.0
margin_bottom = 386.0
custom_fonts/font = SubResource( 6 )
text = "Confirm password:"
[node name="FormInputFields" type="VBoxContainer" parent="FormContainer/MainFormContainer"]
margin_left = 655.0
margin_right = 1155.0
margin_bottom = 393.0
custom_constants/separation = 30
[node name="PlayerName" type="LineEdit" parent="FormContainer/MainFormContainer/FormInputFields"]
margin_right = 500.0
margin_bottom = 78.0
rect_min_size = Vector2( 500, 78 )
custom_fonts/font = SubResource( 7 )
max_length = 30
[node name="Email" type="LineEdit" parent="FormContainer/MainFormContainer/FormInputFields"]
margin_top = 108.0
margin_right = 500.0
margin_bottom = 183.0
rect_min_size = Vector2( 360, 75 )
custom_fonts/font = SubResource( 8 )
max_length = 50
[node name="Password" type="LineEdit" parent="FormContainer/MainFormContainer/FormInputFields"]
margin_top = 213.0
margin_right = 500.0
margin_bottom = 288.0
rect_min_size = Vector2( 360, 75 )
custom_fonts/font = SubResource( 9 )
max_length = 30
secret = true
[node name="ConfirmPassword" type="LineEdit" parent="FormContainer/MainFormContainer/FormInputFields"]
margin_top = 318.0
margin_right = 500.0
margin_bottom = 393.0
rect_min_size = Vector2( 360, 75 )
custom_fonts/font = SubResource( 10 )
max_length = 30
secret = true
[node name="InfoLabels" type="VBoxContainer" parent="FormContainer/MainFormContainer"]
margin_left = 1185.0
margin_right = 1269.0
margin_bottom = 393.0
rect_min_size = Vector2( 40, 0 )
custom_constants/separation = 24
[node name="UsernameToolButton" type="ToolButton" parent="FormContainer/MainFormContainer/InfoLabels"]
margin_right = 84.0
margin_bottom = 80.0
icon = ExtResource( 5 )
[node name="DummyToolButton1" type="ToolButton" parent="FormContainer/MainFormContainer/InfoLabels"]
margin_top = 104.0
margin_right = 84.0
margin_bottom = 184.0
icon = ExtResource( 6 )
[node name="PasswordToolButton" type="ToolButton" parent="FormContainer/MainFormContainer/InfoLabels"]
margin_top = 208.0
margin_right = 84.0
margin_bottom = 288.0
icon = ExtResource( 5 )
[node name="DummyToolButton2" type="ToolButton" parent="FormContainer/MainFormContainer/InfoLabels"]
margin_top = 312.0
margin_right = 84.0
margin_bottom = 392.0
icon = ExtResource( 6 )
[node name="InfoBox" type="Label" parent="FormContainer"]
visible = false
margin_right = 1269.0
margin_bottom = 137.0
rect_min_size = Vector2( 250, 0 )
custom_styles/normal = SubResource( 11 )
custom_fonts/font = SubResource( 12 )
custom_colors/font_color = Color( 0.666667, 0.223529, 0.223529, 1 )
custom_colors/font_outline_modulate = Color( 0.937255, 0.917647, 0.917647, 1 )
text = "Password should contain at least 8 characters including uppercase and lowercase letters, numbers and (optionally) special characters."
autowrap = true
[node name="ErrorMessage" type="Label" parent="FormContainer"]
visible = false
margin_right = 1189.0
margin_bottom = 37.0
custom_fonts/font = SubResource( 13 )
custom_colors/font_color = Color( 0.866667, 0.101961, 0.101961, 1 )
autowrap = true
[node name="RegisterButton" parent="FormContainer" instance=ExtResource( 2 )]
margin_top = 585.0
margin_right = 1269.0
margin_bottom = 703.0
text = "Submit"
[node name="ProcessingLabel" type="Label" parent="FormContainer"]
visible = false
margin_right = 1189.0
margin_bottom = 72.0
custom_fonts/font = SubResource( 14 )
text = "Processing..."
align = 1
[connection signal="pressed" from="BackButton" to="." method="_on_BackButton_pressed"]
[connection signal="mouse_entered" from="FormContainer/MainFormContainer/InfoLabels/UsernameToolButton" to="." method="_on_UsernameToolButton_mouse_entered"]
[connection signal="mouse_exited" from="FormContainer/MainFormContainer/InfoLabels/UsernameToolButton" to="." method="_on_UsernameToolButton_mouse_exited"]
[connection signal="mouse_entered" from="FormContainer/MainFormContainer/InfoLabels/PasswordToolButton" to="." method="_on_PasswordToolButton_mouse_entered"]
[connection signal="mouse_exited" from="FormContainer/MainFormContainer/InfoLabels/PasswordToolButton" to="." method="_on_PasswordToolButton_mouse_exited"]
[connection signal="pressed" from="FormContainer/RegisterButton" to="." method="_on_RegisterButton_pressed"]

View file

@ -0,0 +1,266 @@
[gd_scene load_steps=20 format=2]
[ext_resource path="res://addons/silent_wolf/Auth/Register.gd" type="Script" id=1]
[ext_resource path="res://addons/silent_wolf/common/SWButton.tscn" type="PackedScene" id=2]
[ext_resource path="res://addons/silent_wolf/assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=3]
[ext_resource path="res://assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=4]
[ext_resource path="res://addons/silent_wolf/assets/gfx/info_icon_small.png" type="Texture" id=5]
[ext_resource path="res://addons/silent_wolf/assets/gfx/dummy_info_icon_small.png" type="Texture" id=6]
[sub_resource type="StyleBoxFlat" id=1]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=2]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=3]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.482353, 0.458824, 0.458824, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="DynamicFont" id=4]
size = 64
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=5]
size = 64
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=6]
size = 64
font_data = ExtResource( 4 )
[sub_resource type="DynamicFont" id=7]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=8]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=9]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="StyleBoxFlat" id=10]
content_margin_left = 30.0
content_margin_right = 30.0
content_margin_top = 30.0
content_margin_bottom = 30.0
bg_color = Color( 1, 1, 1, 1 )
border_width_left = 3
border_width_top = 3
border_width_right = 3
border_width_bottom = 3
border_color = Color( 0.666667, 0.223529, 0.223529, 1 )
corner_radius_top_left = 10
corner_radius_top_right = 10
corner_radius_bottom_right = 10
corner_radius_bottom_left = 10
[sub_resource type="DynamicFont" id=11]
size = 32
outline_color = Color( 0.666667, 0.223529, 0.223529, 1 )
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=12]
size = 32
outline_color = Color( 0.854902, 0.0901961, 0.0901961, 1 )
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=13]
size = 32
font_data = ExtResource( 3 )
[node name="Register" type="TextureRect"]
margin_right = 40.0
margin_bottom = 40.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="BackButton" parent="." instance=ExtResource( 2 )]
margin_left = 241.0
margin_top = 54.0
margin_right = 529.0
margin_bottom = 172.0
custom_styles/hover = SubResource( 1 )
custom_styles/pressed = SubResource( 2 )
custom_styles/normal = SubResource( 3 )
custom_fonts/font = SubResource( 4 )
text = "← Back"
[node name="FormContainer" type="VBoxContainer" parent="."]
margin_left = 396.0
margin_top = 200.0
margin_right = 1665.0
margin_bottom = 1060.0
grow_horizontal = 0
grow_vertical = 0
custom_constants/separation = 60
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Label" type="Label" parent="FormContainer"]
margin_right = 1269.0
margin_bottom = 72.0
custom_fonts/font = SubResource( 5 )
text = "Sign up"
align = 1
[node name="MainFormContainer" type="HBoxContainer" parent="FormContainer"]
margin_top = 132.0
margin_right = 1269.0
margin_bottom = 524.0
custom_constants/separation = 30
[node name="FormLabels" type="VBoxContainer" parent="FormContainer/MainFormContainer"]
margin_right = 625.0
margin_bottom = 392.0
custom_constants/separation = 30
[node name="PlayerNameLabel" type="Label" parent="FormContainer/MainFormContainer/FormLabels"]
margin_right = 625.0
margin_bottom = 80.0
rect_min_size = Vector2( 0, 80 )
custom_fonts/font = SubResource( 6 )
text = "Username:"
[node name="PasswordLabel" type="Label" parent="FormContainer/MainFormContainer/FormLabels"]
margin_top = 110.0
margin_right = 625.0
margin_bottom = 182.0
custom_fonts/font = SubResource( 6 )
text = "Password:"
[node name="ConfirmPasswordLabel" type="Label" parent="FormContainer/MainFormContainer/FormLabels"]
margin_top = 212.0
margin_right = 625.0
margin_bottom = 284.0
custom_fonts/font = SubResource( 6 )
text = "Confirm password:"
[node name="FormInputFields" type="VBoxContainer" parent="FormContainer/MainFormContainer"]
margin_left = 655.0
margin_right = 1155.0
margin_bottom = 392.0
custom_constants/separation = 30
[node name="PlayerName" type="LineEdit" parent="FormContainer/MainFormContainer/FormInputFields"]
margin_right = 500.0
margin_bottom = 78.0
rect_min_size = Vector2( 500, 78 )
custom_fonts/font = SubResource( 7 )
max_length = 30
[node name="Password" type="LineEdit" parent="FormContainer/MainFormContainer/FormInputFields"]
margin_top = 108.0
margin_right = 500.0
margin_bottom = 183.0
rect_min_size = Vector2( 360, 75 )
custom_fonts/font = SubResource( 8 )
max_length = 30
secret = true
[node name="ConfirmPassword" type="LineEdit" parent="FormContainer/MainFormContainer/FormInputFields"]
margin_top = 213.0
margin_right = 500.0
margin_bottom = 288.0
rect_min_size = Vector2( 360, 75 )
custom_fonts/font = SubResource( 9 )
max_length = 30
secret = true
[node name="InfoLabels" type="VBoxContainer" parent="FormContainer/MainFormContainer"]
margin_left = 1185.0
margin_right = 1269.0
margin_bottom = 392.0
rect_min_size = Vector2( 40, 0 )
custom_constants/separation = 24
[node name="UsernameToolButton" type="ToolButton" parent="FormContainer/MainFormContainer/InfoLabels"]
margin_right = 84.0
margin_bottom = 80.0
icon = ExtResource( 5 )
[node name="DummyToolButton1" type="ToolButton" parent="FormContainer/MainFormContainer/InfoLabels"]
margin_top = 104.0
margin_right = 84.0
margin_bottom = 184.0
icon = ExtResource( 6 )
[node name="PasswordToolButton" type="ToolButton" parent="FormContainer/MainFormContainer/InfoLabels"]
margin_top = 208.0
margin_right = 84.0
margin_bottom = 288.0
icon = ExtResource( 5 )
[node name="DummyToolButton2" type="ToolButton" parent="FormContainer/MainFormContainer/InfoLabels"]
margin_top = 312.0
margin_right = 84.0
margin_bottom = 392.0
icon = ExtResource( 6 )
[node name="InfoBox" type="Label" parent="FormContainer"]
visible = false
margin_right = 1269.0
margin_bottom = 137.0
rect_min_size = Vector2( 250, 0 )
custom_styles/normal = SubResource( 10 )
custom_fonts/font = SubResource( 11 )
custom_colors/font_color = Color( 0.666667, 0.223529, 0.223529, 1 )
custom_colors/font_outline_modulate = Color( 0.937255, 0.917647, 0.917647, 1 )
text = "Password should contain at least 8 characters including uppercase and lowercase letters, numbers and (optionally) special characters."
autowrap = true
[node name="ErrorMessage" type="Label" parent="FormContainer"]
visible = false
margin_right = 1189.0
margin_bottom = 37.0
custom_fonts/font = SubResource( 12 )
custom_colors/font_color = Color( 0.866667, 0.101961, 0.101961, 1 )
autowrap = true
[node name="RegisterUPButton" parent="FormContainer" instance=ExtResource( 2 )]
margin_top = 584.0
margin_right = 1269.0
margin_bottom = 702.0
text = "Submit"
[node name="ProcessingLabel" type="Label" parent="FormContainer"]
visible = false
margin_right = 1189.0
margin_bottom = 72.0
custom_fonts/font = SubResource( 13 )
text = "Processing..."
align = 1
[connection signal="pressed" from="BackButton" to="." method="_on_BackButton_pressed"]
[connection signal="mouse_entered" from="FormContainer/MainFormContainer/InfoLabels/UsernameToolButton" to="." method="_on_UsernameToolButton_mouse_entered"]
[connection signal="mouse_exited" from="FormContainer/MainFormContainer/InfoLabels/UsernameToolButton" to="." method="_on_UsernameToolButton_mouse_exited"]
[connection signal="mouse_entered" from="FormContainer/MainFormContainer/InfoLabels/PasswordToolButton" to="." method="_on_PasswordToolButton_mouse_entered"]
[connection signal="mouse_exited" from="FormContainer/MainFormContainer/InfoLabels/PasswordToolButton" to="." method="_on_PasswordToolButton_mouse_exited"]
[connection signal="pressed" from="FormContainer/RegisterUPButton" to="." method="_on_RegisterUPButton_pressed"]

View file

@ -0,0 +1,63 @@
extends TextureRect
var player_name = null
var login_scene = "res://addons/silent_wolf/Auth/Login.tscn"
# Called when the node enters the scene tree for the first time.
func _ready():
$"RequestFormContainer/ProcessingLabel".hide()
$"PwdResetFormContainer/ProcessingLabel".hide()
$"PasswordChangedContainer".hide()
$"PwdResetFormContainer".hide()
$"RequestFormContainer".show()
SilentWolf.Auth.connect("sw_request_password_reset_succeeded", self, "_on_send_code_succeeded")
SilentWolf.Auth.connect("sw_request_password_reset_failed", self, "_on_send_code_failed")
SilentWolf.Auth.connect("sw_reset_password_succeeded", self, "_on_reset_succeeded")
SilentWolf.Auth.connect("sw_reset_password_failed", self, "_on_reset_failed")
if "login_scene" in SilentWolf.Auth:
login_scene = SilentWolf.Auth.login_scene
func _on_BackButton_pressed():
get_tree().change_scene(login_scene)
func _on_PlayerNameSubmitButton_pressed():
player_name = $"RequestFormContainer/FormContainer/FormInputFields/PlayerName".text
SilentWolf.Auth.request_player_password_reset(player_name)
$"RequestFormContainer/ProcessingLabel".show()
func _on_send_code_succeeded():
$"RequestFormContainer/ProcessingLabel".hide()
$"RequestFormContainer".hide()
$"PwdResetFormContainer".show()
func _on_send_code_failed(error):
$"RequestFormContainer/ProcessingLabel".hide()
$"RequestFormContainer/ErrorMessage".text = "Could not send confirmation code. " + str(error)
$"RequestFormContainer/ErrorMessage".show()
func _on_NewPasswordSubmitButton_pressed():
var code = $"PwdResetFormContainer/FormContainer/FormInputFields/Code".text
var password = $"PwdResetFormContainer/FormContainer/FormInputFields/Password".text
var confirm_password = $"PwdResetFormContainer/FormContainer/FormInputFields/ConfirmPassword".text
SilentWolf.Auth.reset_player_password(player_name, code, password, confirm_password)
$"PwdResetFormContainer/ProcessingLabel".show()
func _on_reset_succeeded():
$"PwdResetFormContainer/ProcessingLabel".hide()
$"PwdResetFormContainer".hide()
$"PasswordChangedContainer".show()
func _on_reset_failed(error):
$"PwdResetFormContainer/ProcessingLabel".hide()
$"PwdResetFormContainer/ErrorMessage".text = "Could not reset password. " + str(error)
$"PwdResetFormContainer/ErrorMessage".show()
func _on_CloseButton_pressed():
get_tree().change_scene(login_scene)

View file

@ -0,0 +1,395 @@
[gd_scene load_steps=27 format=2]
[ext_resource path="res://addons/silent_wolf/Auth/ResetPassword.gd" type="Script" id=1]
[ext_resource path="res://addons/silent_wolf/common/SWButton.tscn" type="PackedScene" id=2]
[ext_resource path="res://addons/silent_wolf/assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=3]
[ext_resource path="res://assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=6]
[sub_resource type="StyleBoxFlat" id=1]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=2]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=3]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.482353, 0.458824, 0.458824, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="DynamicFont" id=4]
size = 64
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=5]
size = 64
font_data = ExtResource( 6 )
[sub_resource type="DynamicFont" id=6]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=7]
size = 64
font_data = ExtResource( 6 )
[sub_resource type="DynamicFont" id=8]
size = 32
font_data = ExtResource( 6 )
[sub_resource type="DynamicFont" id=9]
size = 32
font_data = ExtResource( 6 )
[sub_resource type="StyleBoxFlat" id=10]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=11]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=12]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.666667, 0.223529, 0.223529, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="DynamicFont" id=13]
size = 64
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=14]
size = 64
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=15]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=16]
size = 64
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=17]
size = 64
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=18]
size = 64
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=19]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=20]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=21]
size = 32
outline_color = Color( 0.854902, 0.0901961, 0.0901961, 1 )
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=22]
size = 32
font_data = ExtResource( 3 )
[node name="ResetPassword" type="TextureRect"]
margin_right = 40.0
margin_bottom = 40.0
script = ExtResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="BackButton" parent="." instance=ExtResource( 2 )]
margin_left = 339.0
margin_top = 117.0
margin_right = 667.0
margin_bottom = 235.0
custom_styles/hover = SubResource( 1 )
custom_styles/pressed = SubResource( 2 )
custom_styles/normal = SubResource( 3 )
custom_fonts/font = SubResource( 4 )
text = "← Back"
[node name="RequestFormContainer" type="VBoxContainer" parent="."]
visible = false
margin_left = 679.0
margin_top = 196.75
margin_right = 1327.0
margin_bottom = 933.75
custom_constants/separation = 65
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Label" type="Label" parent="RequestFormContainer"]
margin_right = 1035.0
margin_bottom = 72.0
custom_fonts/font = SubResource( 5 )
text = "Reset password"
align = 1
[node name="LabelExplainer" type="Label" parent="RequestFormContainer"]
margin_top = 137.0
margin_right = 1035.0
margin_bottom = 174.0
custom_fonts/font = SubResource( 6 )
text = "Please enter your player name below."
autowrap = true
[node name="FormContainer" type="HBoxContainer" parent="RequestFormContainer"]
margin_top = 239.0
margin_right = 1035.0
margin_bottom = 314.0
custom_constants/separation = 50
[node name="FormLabels" type="VBoxContainer" parent="RequestFormContainer/FormContainer"]
margin_right = 426.0
margin_bottom = 75.0
custom_constants/separation = 30
[node name="PlayerNameLabel" type="Label" parent="RequestFormContainer/FormContainer/FormLabels"]
margin_right = 426.0
margin_bottom = 72.0
custom_fonts/font = SubResource( 7 )
text = "Player name:"
[node name="FormInputFields" type="VBoxContainer" parent="RequestFormContainer/FormContainer"]
margin_left = 476.0
margin_right = 836.0
margin_bottom = 75.0
custom_constants/separation = 30
[node name="PlayerName" type="LineEdit" parent="RequestFormContainer/FormContainer/FormInputFields"]
margin_right = 360.0
margin_bottom = 75.0
rect_min_size = Vector2( 360, 75 )
custom_fonts/font = SubResource( 8 )
max_length = 50
[node name="ErrorMessage" type="Label" parent="RequestFormContainer"]
visible = false
margin_top = 522.0
margin_right = 648.0
margin_bottom = 559.0
custom_fonts/font = SubResource( 9 )
custom_colors/font_color = Color( 0.866667, 0.101961, 0.101961, 1 )
autowrap = true
[node name="PlayerNameSubmitButton" parent="RequestFormContainer" instance=ExtResource( 2 )]
margin_top = 379.0
margin_right = 1035.0
margin_bottom = 497.0
custom_styles/hover = SubResource( 10 )
custom_styles/pressed = SubResource( 11 )
custom_styles/normal = SubResource( 12 )
custom_fonts/font = SubResource( 13 )
text = "Submit"
[node name="ProcessingLabel" type="Label" parent="RequestFormContainer"]
visible = false
margin_top = 740.0
margin_right = 648.0
margin_bottom = 812.0
custom_fonts/font = SubResource( 5 )
text = "Processing..."
align = 1
[node name="PwdResetFormContainer" type="VBoxContainer" parent="."]
margin_left = 679.0
margin_top = 196.75
margin_right = 1714.0
margin_bottom = 973.75
custom_constants/separation = 70
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Label" type="Label" parent="PwdResetFormContainer"]
margin_right = 1035.0
margin_bottom = 72.0
custom_fonts/font = SubResource( 14 )
text = "Reset password"
align = 1
[node name="LabelExplainer" type="Label" parent="PwdResetFormContainer"]
margin_top = 142.0
margin_right = 1035.0
margin_bottom = 219.0
custom_fonts/font = SubResource( 15 )
text = "Please enter below the code we sent you by email and your new password twice."
autowrap = true
[node name="FormContainer" type="HBoxContainer" parent="PwdResetFormContainer"]
margin_top = 289.0
margin_right = 1035.0
margin_bottom = 574.0
custom_constants/separation = 50
[node name="FormLabels" type="VBoxContainer" parent="PwdResetFormContainer/FormContainer"]
margin_right = 625.0
margin_bottom = 285.0
custom_constants/separation = 30
[node name="CodeLabel" type="Label" parent="PwdResetFormContainer/FormContainer/FormLabels"]
margin_right = 625.0
margin_bottom = 72.0
custom_fonts/font = SubResource( 16 )
text = "Code:"
[node name="PasswordLabel" type="Label" parent="PwdResetFormContainer/FormContainer/FormLabels"]
margin_top = 102.0
margin_right = 625.0
margin_bottom = 174.0
custom_fonts/font = SubResource( 17 )
text = "Password:"
[node name="ConfirmPasswordLabel" type="Label" parent="PwdResetFormContainer/FormContainer/FormLabels"]
margin_top = 204.0
margin_right = 625.0
margin_bottom = 276.0
custom_fonts/font = SubResource( 18 )
text = "Confirm password:"
[node name="FormInputFields" type="VBoxContainer" parent="PwdResetFormContainer/FormContainer"]
margin_left = 675.0
margin_right = 1035.0
margin_bottom = 285.0
custom_constants/separation = 30
[node name="Code" type="LineEdit" parent="PwdResetFormContainer/FormContainer/FormInputFields"]
margin_right = 360.0
margin_bottom = 75.0
rect_min_size = Vector2( 360, 75 )
custom_fonts/font = SubResource( 8 )
max_length = 50
[node name="Password" type="LineEdit" parent="PwdResetFormContainer/FormContainer/FormInputFields"]
margin_top = 105.0
margin_right = 360.0
margin_bottom = 180.0
rect_min_size = Vector2( 360, 75 )
custom_fonts/font = SubResource( 19 )
max_length = 30
secret = true
[node name="ConfirmPassword" type="LineEdit" parent="PwdResetFormContainer/FormContainer/FormInputFields"]
margin_top = 210.0
margin_right = 360.0
margin_bottom = 285.0
rect_min_size = Vector2( 360, 75 )
custom_fonts/font = SubResource( 20 )
max_length = 30
secret = true
[node name="ErrorMessage" type="Label" parent="PwdResetFormContainer"]
visible = false
margin_top = 522.0
margin_right = 648.0
margin_bottom = 559.0
custom_fonts/font = SubResource( 21 )
custom_colors/font_color = Color( 0.866667, 0.101961, 0.101961, 1 )
autowrap = true
[node name="NewPasswordSubmitButton" parent="PwdResetFormContainer" instance=ExtResource( 2 )]
margin_top = 644.0
margin_right = 1035.0
margin_bottom = 762.0
custom_styles/hover = SubResource( 10 )
custom_styles/pressed = SubResource( 11 )
custom_styles/normal = SubResource( 12 )
custom_fonts/font = SubResource( 13 )
text = "Submit"
[node name="ProcessingLabel" type="Label" parent="PwdResetFormContainer"]
visible = false
margin_top = 740.0
margin_right = 648.0
margin_bottom = 812.0
custom_fonts/font = SubResource( 22 )
text = "Processing..."
align = 1
[node name="PasswordChangedContainer" type="VBoxContainer" parent="."]
visible = false
margin_left = 679.0
margin_top = 312.0
margin_right = 1327.0
margin_bottom = 933.0
custom_constants/separation = 100
__meta__ = {
"_edit_use_anchors_": false
}
[node name="PwdChanedLabel" type="Label" parent="PasswordChangedContainer"]
margin_right = 524.0
margin_bottom = 72.0
custom_fonts/font = SubResource( 5 )
text = "Password reset"
align = 1
[node name="PasswordChangedLabelExplainer" type="Label" parent="PasswordChangedContainer"]
margin_top = 76.0
margin_right = 524.0
margin_bottom = 153.0
custom_fonts/font = SubResource( 15 )
text = "Your password was changed successfully."
autowrap = true
[node name="CloseButton" parent="PasswordChangedContainer" instance=ExtResource( 2 )]
margin_top = 157.0
margin_right = 524.0
margin_bottom = 275.0
custom_styles/hover = SubResource( 10 )
custom_styles/pressed = SubResource( 11 )
custom_styles/normal = SubResource( 12 )
custom_fonts/font = SubResource( 13 )
text = "Close"
[connection signal="pressed" from="BackButton" to="." method="_on_BackButton_pressed"]
[connection signal="pressed" from="RequestFormContainer/PlayerNameSubmitButton" to="." method="_on_PlayerNameSubmitButton_pressed"]
[connection signal="pressed" from="PwdResetFormContainer/NewPasswordSubmitButton" to="." method="_on_NewPasswordSubmitButton_pressed"]
[connection signal="pressed" from="PasswordChangedContainer/CloseButton" to="." method="_on_CloseButton_pressed"]

BIN
addons/silent_wolf/Multiplayer/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,39 @@
extends Node
onready var WSClient = Node.new()
var mp_ws_ready = false
var mp_session_started = false
var mp_player_name = ""
func _ready():
mp_ws_ready = false
mp_session_started = false
var ws_client_script = load("res://addons/silent_wolf/Multiplayer/ws/WSClient.gd")
WSClient.set_script(ws_client_script)
add_child(WSClient)
func init_mp_session(player_name):
#mp_player_name = player_name
WSClient.init_mp_session(player_name)
# TODO: instead of waiting an arbitrary amount of time, yield on
# a function that guarantees that child ready() function has run
#yield(get_tree().create_timer(0.3), "timeout")
func _send_init_message():
WSClient.init_mp_session(mp_player_name)
mp_ws_ready = true
mp_session_started = true
func send(data: Dictionary):
# First check that WSClient is in tree
print("Attempting to send data to web socket server")
if WSClient.is_inside_tree():
# TODO: check if data is properly formatted (should be dictionary?)
print("Sending data to web socket server...")
WSClient.send_to_server("update", data)

View file

@ -0,0 +1,81 @@
extends Node
const SWLogger = preload("res://addons/silent_wolf/utils/SWLogger.gd")
# The URL we will connect to
export var websocket_url = "wss://ws.silentwolfmp.com/server"
export var ws_room_init_url = "wss://ws.silentwolfmp.com/init"
signal ws_client_ready
# Our WebSocketClient instance
var _client = WebSocketClient.new()
func _ready():
SWLogger.debug("Entering MPClient _ready function")
# Connect base signals to get notified of connection open, close, and errors.
_client.connect("connection_closed", self, "_closed")
_client.connect("connection_error", self, "_closed")
_client.connect("connection_established", self, "_connected")
# This signal is emitted when not using the Multiplayer API every time
# a full packet is received.
# Alternatively, you could check get_peer(1).get_available_packets() in a loop.
_client.connect("data_received", self, "_on_data")
# Initiate connection to the given URL.
var err = _client.connect_to_url(websocket_url)
if err != OK:
#SWLogger.debug("Unable to connect to WS server")
print("Unable to connect to WS server")
set_process(false)
emit_signal("ws_client_ready")
func _closed(was_clean = false):
# was_clean will tell you if the disconnection was correctly notified
# by the remote peer before closing the socket.
SWLogger.debug("WS connection closed, clean: " + str(was_clean))
set_process(false)
func _connected(proto = ""):
# This is called on connection, "proto" will be the selected WebSocket
# sub-protocol (which is optional)
#SWLogger.debug("Connected with protocol: " + str(proto))
print("Connected with protocol: ", proto)
# You MUST always use get_peer(1).put_packet to send data to server,
# and not put_packet directly when not using the MultiplayerAPI.
#var test_packet = { "data": "Test packet" }
#send_to_server(test_packet)
#_client.get_peer(1).put_packet("Test packet".to_utf8())
func _on_data():
# Print the received packet, you MUST always use get_peer(1).get_packet
# to receive data from server, and not get_packet directly when not
# using the MultiplayerAPI.
#SWLogger.debug("Got data from WS server: " + str(_client.get_peer(1).get_packet().get_string_from_utf8()))
print("Got data from WS server: ", _client.get_peer(1).get_packet().get_string_from_utf8())
func _process(delta):
# Call this in _process or _physics_process. Data transfer, and signals
# emission will only happen when calling this function.
_client.poll()
# send arbitrary data to backend
func send_to_server(message_type, data):
data["message_type"] = message_type
print("Sending data to server: " + str(data))
_client.get_peer(1).put_packet(str(JSON.print(data)).to_utf8())
func init_mp_session(player_name):
print("WSClient init_mp_session, sending initialisation packet to server")
var init_packet = {
"player_name": player_name
}
return send_to_server("init", init_packet)
func create_room():
pass

View file

@ -0,0 +1,199 @@
extends Node
const CommonErrors = preload("res://addons/silent_wolf/common/CommonErrors.gd")
const SWLogger = preload("res://addons/silent_wolf/utils/SWLogger.gd")
signal sw_player_data_received
signal sw_player_data_posted
signal sw_player_data_removed
var GetPlayerData = null
var PushPlayerData = null
var RemovePlayerData = null
# wekrefs
var wrGetPlayerData = null
var wrPushPlayerData = null
var wrRemovePlayerData = null
var player_name = null
var player_data = null
# Called when the node enters the scene tree for the first time.
func _ready():
pass # Replace with function body.
func set_player_data(new_player_data):
player_data = new_player_data
func clear_player_data():
player_name = null
player_data = null
func get_stats():
var stats = null
if player_data:
stats = {
"strength": player_data.strength,
"speed": player_data.speed,
"reflexes": player_data.reflexes,
"max_health": player_data.max_health,
"career": player_data.career
}
return stats
func get_inventory():
var inventory = null
if player_data:
inventory = {
"weapons": player_data.weapons,
"gold": player_data.gold
}
return inventory
func get_player_data(player_name):
GetPlayerData = HTTPRequest.new()
wrGetPlayerData = weakref(GetPlayerData)
if OS.get_name() != "HTML5":
GetPlayerData.set_use_threads(true)
get_tree().get_root().add_child(GetPlayerData)
GetPlayerData.connect("request_completed", self, "_on_GetPlayerData_request_completed")
SWLogger.info("Calling SilentWolf to get player data")
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var api_key = SilentWolf.config.api_key
var headers = [
"x-api-key: " + api_key,
"x-sw-plugin-version: " + SilentWolf.version,
"x-sw-game-id: " + SilentWolf.config.game_id,
"x-sw-godot-version: " + SilentWolf.godot_version
]
GetPlayerData.request("https://api.silentwolf.com/get_player_data/" + game_id + "/" + player_name, headers, true, HTTPClient.METHOD_GET)
return self
func post_player_data(player_name, player_data, overwrite=true):
if typeof(player_data) != TYPE_DICTIONARY:
SWLogger.error("Player data should be of type Dictionary, instead it is of type: " + str(typeof(player_data)))
PushPlayerData = HTTPRequest.new()
wrPushPlayerData = weakref(PushPlayerData)
if OS.get_name() != "HTML5":
PushPlayerData.set_use_threads(true)
get_tree().get_root().add_child(PushPlayerData)
PushPlayerData.connect("request_completed", self, "_on_PushPlayerData_request_completed")
SWLogger.info("Calling SilentWolf to post player data")
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var api_key = SilentWolf.config.api_key
var headers = [
"Content-Type: application/json",
"x-api-key: " + api_key,
"x-sw-plugin-version: " + SilentWolf.version,
"x-sw-game-id: " + SilentWolf.config.game_id,
"x-sw-godot-version: " + SilentWolf.godot_version
]
var payload = { "game_id": game_id, "game_version": game_version, "player_name": player_name, "player_data": player_data, "overwrite": overwrite }
var query = JSON.print(payload)
PushPlayerData.request("https://api.silentwolf.com/push_player_data", headers, true, HTTPClient.METHOD_POST, query)
return self
func delete_player_weapons(player_name):
var weapons = { "Weapons": [] }
delete_player_data(player_name, weapons)
func remove_player_money(player_name):
var money = { "Money": 0 }
delete_player_data(player_name, money)
func delete_player_items(player_name, item_name):
var item = { item_name: "" }
delete_player_data(player_name, item)
func delete_all_player_data(player_name):
delete_player_data(player_name, "")
func delete_player_data(player_name, player_data):
RemovePlayerData = HTTPRequest.new()
wrRemovePlayerData = weakref(RemovePlayerData)
if OS.get_name() != "HTML5":
RemovePlayerData.set_use_threads(true)
get_tree().get_root().add_child(RemovePlayerData)
RemovePlayerData.connect("request_completed", self, "_on_RemovePlayerData_request_completed")
SWLogger.info("Calling SilentWolf to remove player data")
var game_id = SilentWolf.config.game_id
var api_key = SilentWolf.config.api_key
var headers = [
"Content-Type: application/json",
"x-api-key: " + api_key,
"x-sw-plugin-version: " + SilentWolf.version,
"x-sw-game-id: " + SilentWolf.config.game_id,
"x-sw-godot-version: " + SilentWolf.godot_version
]
var payload = { "game_id": game_id, "player_name": player_name, "player_data": player_data }
var query = JSON.print(payload)
RemovePlayerData.request("https://api.silentwolf.com/remove_player_data", headers, true, HTTPClient.METHOD_POST, query)
return self
func _on_GetPlayerData_request_completed(result, response_code, headers, body):
SWLogger.info("GetPlayerData request completed")
var status_check = CommonErrors.check_status_code(response_code)
#print("client status: " + str(GetPlayerData.get_http_client_status()))
if is_instance_valid(GetPlayerData):
SilentWolf.free_request(wrGetPlayerData, GetPlayerData)
#GetPlayerData.queue_free()
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/playerdata")
else:
SWLogger.info("SilentWolf get player data success")
player_name = response.player_name
player_data = response.player_data
SWLogger.debug("Request completed: Player data: " + str(player_data))
emit_signal("sw_player_data_received", player_name, player_data)
func _on_PushPlayerData_request_completed(result, response_code, headers, body):
SWLogger.info("PushPlayerData request completed")
var status_check = CommonErrors.check_status_code(response_code)
if is_instance_valid(PushPlayerData):
#PushPlayerData.queue_free()
SilentWolf.free_request(wrPushPlayerData, PushPlayerData)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/playerdata")
else:
SWLogger.info("SilentWolf post player data score success: " + str(response_code))
var player_name = response.player_name
emit_signal("sw_player_data_posted", player_name)
func _on_RemovePlayerData_request_completed(result, response_code, headers, body):
SWLogger.info("RemovePlayerData request completed")
var status_check = CommonErrors.check_status_code(response_code)
if is_instance_valid(RemovePlayerData):
RemovePlayerData.queue_free()
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/playerdata")
else:
SWLogger.info("SilentWolf post player data score success: " + str(response_code))
var player_name = response.player_name
# return player_data after (maybe partial) removal
var player_data = response.player_data
emit_signal("sw_player_data_removed", player_name, player_data)

BIN
addons/silent_wolf/Scores/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,126 @@
tool
extends Node2D
const ScoreItem = preload("ScoreItem.tscn")
const SWLogger = preload("res://addons/silent_wolf/utils/SWLogger.gd")
var list_index = 0
# Replace the leaderboard name if you're not using the default leaderboard
var ld_name = "main"
var max_scores = 10
func _ready():
print("SilentWolf.Scores.leaderboards: " + str(SilentWolf.Scores.leaderboards))
print("SilentWolf.Scores.ldboard_config: " + str(SilentWolf.Scores.ldboard_config))
#var scores = SilentWolf.Scores.scores
var scores = []
if ld_name in SilentWolf.Scores.leaderboards:
scores = SilentWolf.Scores.leaderboards[ld_name]
var local_scores = SilentWolf.Scores.local_scores
if len(scores) > 0:
render_board(scores, local_scores)
else:
# use a signal to notify when the high scores have been returned, and show a "loading" animation until it's the case...
add_loading_scores_message()
yield(SilentWolf.Scores.get_high_scores(), "sw_scores_received")
hide_message()
render_board(SilentWolf.Scores.scores, local_scores)
func render_board(scores, local_scores):
var all_scores = scores
if ld_name in SilentWolf.Scores.ldboard_config and is_default_leaderboard(SilentWolf.Scores.ldboard_config[ld_name]):
all_scores = merge_scores_with_local_scores(scores, local_scores, max_scores)
if !scores and !local_scores:
add_no_scores_message()
else:
if !scores:
add_no_scores_message()
if !all_scores:
for score in scores:
add_item(score.player_name, str(int(score.score)))
else:
for score in all_scores:
add_item(score.player_name, str(int(score.score)))
func is_default_leaderboard(ld_config):
var default_insert_opt = (ld_config.insert_opt == "keep")
var not_time_based = !("time_based" in ld_config)
return default_insert_opt and not_time_based
func merge_scores_with_local_scores(scores, local_scores, max_scores=10):
if local_scores:
for score in local_scores:
var in_array = score_in_score_array(scores, score)
if !in_array:
scores.append(score)
scores.sort_custom(self, "sort_by_score");
var return_scores = scores
if scores.size() > max_scores:
return_scores = scores.resize(max_scores)
return return_scores
func sort_by_score(a, b):
if a.score > b.score:
return true;
else:
if a.score < b.score:
return false;
else:
return true;
func score_in_score_array(scores, new_score):
var in_score_array = false
if new_score and scores:
for score in scores:
if score.score_id == new_score.score_id: # score.player_name == new_score.player_name and score.score == new_score.score:
in_score_array = true
return in_score_array
func add_item(player_name, score):
var item = ScoreItem.instance()
list_index += 1
item.get_node("PlayerName").text = str(list_index) + str(". ") + player_name
item.get_node("Score").text = score
item.margin_top = list_index * 100
$"Board/HighScores/ScoreItemContainer".add_child(item)
func add_no_scores_message():
var item = $"Board/MessageContainer/TextMessage"
item.text = "No scores yet!"
$"Board/MessageContainer".show()
item.margin_top = 135
func add_loading_scores_message():
var item = $"Board/MessageContainer/TextMessage"
item.text = "Loading scores..."
$"Board/MessageContainer".show()
item.margin_top = 135
func hide_message():
$"Board/MessageContainer".hide()
func clear_leaderboard():
var score_item_container = $"Board/HighScores/ScoreItemContainer"
if score_item_container.get_child_count() > 0:
var children = score_item_container.get_children()
for c in children:
score_item_container.remove_child(c)
c.queue_free()
func _on_CloseButton_pressed():
var scene_name = SilentWolf.scores_config.open_scene_on_close
SWLogger.info("Closing SilentWolf leaderboard, switching to scene: " + str(scene_name))
#global.reset()
get_tree().change_scene(scene_name)

View file

@ -0,0 +1,88 @@
[gd_scene load_steps=8 format=2]
[ext_resource path="res://addons/silent_wolf/Scores/Leaderboard.gd" type="Script" id=1]
[ext_resource path="res://addons/silent_wolf/Scores/assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=2]
[ext_resource path="res://addons/silent_wolf/assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=3]
[ext_resource path="res://addons/silent_wolf/common/SWButton.tscn" type="PackedScene" id=4]
[sub_resource type="DynamicFont" id=1]
size = 76
font_data = ExtResource( 2 )
[sub_resource type="DynamicFont" id=2]
size = 32
font_data = ExtResource( 3 )
[sub_resource type="Theme" id=3]
[node name="Leaderboard" type="Node2D"]
script = ExtResource( 1 )
[node name="OldBoard" type="MarginContainer" parent="."]
visible = false
margin_left = 50.0
margin_right = 50.0
margin_bottom = 40.0
[node name="HighScores" type="TextureRect" parent="OldBoard"]
margin_bottom = 40.0
[node name="Board" type="VBoxContainer" parent="."]
margin_left = 20.0
margin_top = 20.0
margin_right = 1884.0
margin_bottom = 1071.0
custom_constants/separation = 0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="TitleContainer" type="CenterContainer" parent="Board"]
margin_right = 1864.0
margin_bottom = 85.0
[node name="Label" type="Label" parent="Board/TitleContainer"]
margin_left = 667.0
margin_right = 1196.0
margin_bottom = 85.0
custom_fonts/font = SubResource( 1 )
text = "Leaderboard"
[node name="MessageContainer" type="CenterContainer" parent="Board"]
visible = false
margin_top = 89.0
margin_right = 1864.0
margin_bottom = 126.0
[node name="TextMessage" type="Label" parent="Board/MessageContainer"]
margin_left = 789.0
margin_right = 1075.0
margin_bottom = 37.0
custom_fonts/font = SubResource( 2 )
text = "Loading scores..."
valign = 1
[node name="HighScores" type="CenterContainer" parent="Board"]
margin_top = 85.0
margin_right = 1864.0
margin_bottom = 185.0
rect_min_size = Vector2( 0, 100 )
theme = SubResource( 3 )
[node name="ScoreItemContainer" type="VBoxContainer" parent="Board/HighScores"]
margin_left = 932.0
margin_top = 50.0
margin_right = 932.0
margin_bottom = 50.0
[node name="CloseButtonContainer" type="CenterContainer" parent="Board"]
margin_top = 185.0
margin_right = 1864.0
margin_bottom = 303.0
[node name="CloseButton" parent="Board/CloseButtonContainer" instance=ExtResource( 4 )]
margin_left = 582.0
margin_right = 1281.0
margin_bottom = 118.0
text = "Close Leaderboard"
[connection signal="pressed" from="Board/CloseButtonContainer/CloseButton" to="." method="_on_CloseButton_pressed"]

View file

@ -0,0 +1,48 @@
[gd_scene load_steps=5 format=2]
[ext_resource path="res://addons/silent_wolf/assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=2]
[sub_resource type="StyleBoxFlat" id=1]
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
[sub_resource type="DynamicFont" id=2]
size = 32
font_data = ExtResource( 2 )
[sub_resource type="DynamicFont" id=3]
size = 40
use_mipmaps = true
use_filter = true
font_data = ExtResource( 2 )
[node name="ScoreItem" type="Panel"]
margin_right = 782.0
margin_bottom = 76.0
grow_horizontal = 0
grow_vertical = 0
rect_min_size = Vector2( 782, 76 )
custom_styles/panel = SubResource( 1 )
__meta__ = {
"_edit_use_anchors_": false
}
[node name="PlayerName" type="RichTextLabel" parent="."]
margin_left = 22.0
margin_top = 18.0
margin_right = 589.0
margin_bottom = 73.0
custom_fonts/normal_font = SubResource( 2 )
text = "1. Godzilla"
[node name="Score" type="Label" parent="."]
margin_left = 671.0
margin_top = 17.0
margin_right = 756.0
margin_bottom = 65.0
custom_fonts/font = SubResource( 3 )
text = "13"
align = 2
valign = 1
__meta__ = {
"_edit_use_anchors_": false
}

View file

@ -0,0 +1,528 @@
extends Node
const CommonErrors = preload("res://addons/silent_wolf/common/CommonErrors.gd")
const SWLogger = preload("res://addons/silent_wolf/utils/SWLogger.gd")
const UUID = preload("res://addons/silent_wolf/utils/UUID.gd")
const SWHashing = preload("res://addons/silent_wolf/utils/SWHashing.gd")
# legacy signals
signal scores_received
signal position_received
signal score_posted
# new signals
signal sw_scores_received
signal sw_player_scores_received
signal sw_top_player_score_received
signal sw_position_received
signal sw_scores_around_received
signal sw_score_posted
signal sw_leaderboard_wiped
signal sw_score_deleted
# leaderboard scores by leaderboard name
var leaderboards = {}
# leaderboard scores from past periods by leaderboard name and period_offset (negative integers)
var leaderboards_past_periods = {}
# leaderboard configurations by leaderboard name
var ldboard_config = {}
# contains only the scores from one leaderboard at a time
var scores = []
var player_scores = []
var player_top_score = null
var local_scores = []
#var custom_local_scores = []
var score_id = ""
var position = 0
var scores_above = []
var scores_below = []
#var request_timeout = 3
#var request_timer = null
# latest number of scores to be fetched from the backend
var latest_max = 10
var ScorePosition = null
var ScoresAround = null
var HighScores = null
var ScoresByPlayer = null
var TopScoreByPlayer = null
var PostScore = null
var WipeLeaderboard = null
var DeleteScore = null
# wekrefs
var wrScorePosition = null
var wrScoresAround = null
var wrHighScores = null
var wrScoresByPlayer = null
var wrTopScoreByPlayer = null
var wrPostScore = null
var wrWipeLeaderboard = null
var wrDeleteScore = null
# Called when the node enters the scene tree for the first time.
func _ready():
pass
#connect("request_completed", self, "_on_Scores_request_completed")
#setup_request_timer()
func get_score_position(score, ldboard_name="main"):
var score_id = null
var score_value = null
print("score: " + str(score))
if UUID.is_uuid(str(score)):
score_id = score
else:
score_value = score
ScorePosition = HTTPRequest.new()
wrScorePosition = weakref(ScorePosition)
if OS.get_name() != "HTML5":
ScorePosition.set_use_threads(true)
get_tree().get_root().call_deferred("add_child", ScorePosition)
ScorePosition.connect("request_completed", self, "_on_GetScorePosition_request_completed")
SWLogger.info("Calling SilentWolf to get score position")
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var payload = { "game_id": game_id, "game_version": game_version, "ldboard_name": ldboard_name }
if score_id:
payload["score_id"] = score_id
if score_value:
payload["score_value"] = score_value
var request_url = "https://api.silentwolf.com/get_score_position"
send_post_request(ScorePosition, request_url, payload)
return self
func get_scores_around(score, scores_to_fetch=3, ldboard_name="main"):
var score_id = null
var score_value = null
print("score: " + str(score))
if UUID.is_uuid(str(score)):
score_id = score
else:
score_value = score
ScoresAround = HTTPRequest.new()
wrScoresAround = weakref(ScoresAround)
if OS.get_name() != "HTML5":
ScoresAround.set_use_threads(true)
get_tree().get_root().call_deferred("add_child",ScoresAround)
ScoresAround.connect("request_completed", self, "_on_ScoresAround_request_completed")
SWLogger.info("Calling SilentWolf backend to scores above and below a certain score...")
# resetting the latest_number value in case the first requests times out, we need to request the same amount of top scores in the retry
#latest_max = maximum
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var request_url = "https://api.silentwolf.com/get_scores_around/" + str(game_id) + "?version=" + str(game_version) + "&scores_to_fetch=" + str(scores_to_fetch) + "&ldboard_name=" + str(ldboard_name) + "&score_id=" + str(score_id) + "&score_value=" + str(score_value)
send_get_request(ScoresAround, request_url)
return self
func get_high_scores(maximum=10, ldboard_name="main", period_offset=0):
HighScores = HTTPRequest.new()
wrHighScores = weakref(HighScores)
if OS.get_name() != "HTML5":
HighScores.set_use_threads(true)
get_tree().get_root().call_deferred("add_child", HighScores)
HighScores.connect("request_completed", self, "_on_GetHighScores_request_completed")
SWLogger.info("Calling SilentWolf backend to get scores...")
# resetting the latest_number value in case the first requests times out, we need to request the same amount of top scores in the retry
latest_max = maximum
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var request_url = "https://api.silentwolf.com/get_top_scores/" + str(game_id) + "?version=" + str(game_version) + "&max=" + str(maximum) + "&ldboard_name=" + str(ldboard_name) + "&period_offset=" + str(period_offset)
send_get_request(HighScores, request_url)
return self
func get_scores_by_player(player_name, maximum=10, ldboard_name="main", period_offset=0):
print("get_scores_by_player, player_name = " + str(player_name))
if player_name == null:
SWLogger.error("Error in SilentWolf.Scores.get_scores_by_player: provided player_name is null")
else:
ScoresByPlayer = HTTPRequest.new()
wrScoresByPlayer = weakref(ScoresByPlayer)
if OS.get_name() != "HTML5":
ScoresByPlayer.set_use_threads(true)
get_tree().get_root().call_deferred("add_child", ScoresByPlayer)
ScoresByPlayer.connect("request_completed", self, "_on_GetScoresByPlayer_request_completed")
SWLogger.info("Calling SilentWolf backend to get scores for player: " + str(player_name))
# resetting the latest_number value in case the first requests times out, we need to request the same amount of top scores in the retry
latest_max = maximum
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
print("ldboard_name: " + str(ldboard_name))
var request_url = "https://api.silentwolf.com/get_scores_by_player/" + str(game_id) + "?version=" + str(game_version) + "&max=" + str(maximum) + "&ldboard_name=" + str(ldboard_name.percent_encode()) + "&player_name=" + str(player_name.percent_encode()) + "&period_offset=" + str(period_offset)
send_get_request(ScoresByPlayer, request_url)
return self
func get_top_score_by_player(player_name, maximum=10, ldboard_name="main", period_offset=0):
SWLogger.info("get_top_score_by_player, player_name = " + str(player_name))
if player_name == null:
SWLogger.error("Error in SilentWolf.Scores.get_top_score_by_player: provided player_name is null")
else:
TopScoreByPlayer = HTTPRequest.new()
wrTopScoreByPlayer = weakref(TopScoreByPlayer)
if OS.get_name() != "HTML5":
TopScoreByPlayer.set_use_threads(true)
get_tree().get_root().call_deferred("add_child", TopScoreByPlayer)
TopScoreByPlayer.connect("request_completed", self, "_on_GetTopScoreByPlayer_request_completed")
SWLogger.info("Calling SilentWolf backend to get scores for player: " + str(player_name))
# resetting the latest_number value in case the first requests times out, we need to request the same amount of top scores in the retry
latest_max = maximum
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var request_url = "https://api.silentwolf.com/get_top_score_by_player/" + str(game_id) + "?version=" + str(game_version) + "&max=" + str(maximum) + "&ldboard_name=" + str(ldboard_name.percent_encode()) + "&player_name=" + str(player_name.percent_encode()) + "&period_offset=" + str(period_offset)
send_get_request(TopScoreByPlayer, request_url)
return self
func add_to_local_scores(game_result, ld_name="main"):
var local_score = { "score_id": game_result.score_id, "game_id_version" : game_result.game_id + ";" + game_result.game_version, "player_name": game_result.player_name, "score": game_result.score }
local_scores.append(local_score)
#if ld_name == "main":
# TODO: even here, since the main leader board can be customized, we can't just blindly write to the local_scores variable and pull up the scores later
# we need to know what type of leader board it is, or local caching is useless
#local_scores.append(local_score)
#else:
#if ld_name in custom_local_scores:
# TODO: problem: can't just append here - what if it's a highest/latest/accumulator/time-based leaderboard?
# maybe don't use local scores for these special cases? performance?
#custom_local_scores[ld_name].append(local_score)
#else:
#custom_local_scores[ld_name] = [local_score]
SWLogger.debug("local scores: " + str(local_scores))
# metadata, if included should be a dictionary
func persist_score(player_name, score, ldboard_name="main", metadata={}):
# player_name must be present
if player_name == null or player_name == "":
SWLogger.error("ERROR in SilentWolf.Scores.persist_score - please enter a valid player name")
elif typeof(ldboard_name) != TYPE_STRING:
# check that ldboard_name, if present is a String
SWLogger.error("ERROR in ilentWolf.Scores.persist_score - leaderboard name must be a String")
elif typeof(metadata) != TYPE_DICTIONARY:
# check that metadata, if present, is a dictionary
SWLogger.error("ERROR in SilentWolf.Scores.persist_score - metadata must be a dictionary")
else:
PostScore = HTTPRequest.new()
wrPostScore = weakref(PostScore)
if OS.get_name() != "HTML5":
PostScore.set_use_threads(true)
get_tree().get_root().call_deferred("add_child", PostScore)
PostScore.connect("request_completed", self, "_on_PostNewScore_request_completed")
SWLogger.info("Calling SilentWolf backend to post new score...")
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var score_uuid = UUID.generate_uuid_v4()
score_id = score_uuid
var payload = {
"score_id" : score_id,
"player_name" : player_name,
"game_id": game_id,
"game_version": game_version,
"score": score,
"ldboard_name": ldboard_name
}
print("!metadata.empty(): " + str(!metadata.empty()))
if !metadata.empty():
print("metadata: " + str(metadata))
payload["metadata"] = metadata
SWLogger.debug("payload: " + str(payload))
# also add to local scores
add_to_local_scores(payload)
var request_url = "https://api.silentwolf.com/post_new_score"
send_post_request(PostScore, request_url, payload)
return self
# Deletes all your scores for your game and version
# Scores are permanently deleted, no going back!
func wipe_leaderboard(ldboard_name='main'):
WipeLeaderboard = HTTPRequest.new()
wrWipeLeaderboard = weakref(WipeLeaderboard)
if OS.get_name() != "HTML5":
WipeLeaderboard.set_use_threads(true)
get_tree().get_root().call_deferred("add_child", WipeLeaderboard)
WipeLeaderboard.connect("request_completed", self, "_on_WipeLeaderboard_request_completed")
SWLogger.info("Calling SilentWolf backend to wipe leaderboard...")
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var payload = { "game_id": game_id, "game_version": game_version, "ldboard_name": ldboard_name }
var request_url = "https://api.silentwolf.com/wipe_leaderboard"
send_post_request(WipeLeaderboard, request_url, payload)
return self
func delete_score(score_id):
DeleteScore = HTTPRequest.new()
wrDeleteScore = weakref(DeleteScore)
if OS.get_name() != "HTML5":
DeleteScore.set_use_threads(true)
get_tree().get_root().call_deferred("add_child", DeleteScore)
DeleteScore.connect("request_completed", self, "_on_DeleteScore_request_completed")
SWLogger.info("Calling SilentWolf to delete a score")
var game_id = SilentWolf.config.game_id
var game_version = SilentWolf.config.game_version
var request_url = "https://api.silentwolf.com/delete_score?game_id=" + str(game_id) + "&game_version=" + str(game_version) + "&score_id=" + str(score_id)
send_get_request(DeleteScore, request_url)
return self
func _on_GetScoresByPlayer_request_completed(result, response_code, headers, body):
SWLogger.info("GetScoresByPlayer request completed")
var status_check = CommonErrors.check_status_code(response_code)
#print("client status: " + str(HighScores.get_http_client_status()))
#HighScores.queue_free()
SilentWolf.free_request(wrScoresByPlayer, ScoresByPlayer)
SWLogger.debug("response code: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
print("json: " + str(json))
var response = json.result
if response == null:
SWLogger.error("No data returned in GetScoresByPlayer response. Leaderboard may be empty")
emit_signal("sw_player_scores_received", 'No Leaderboard found', scores)
elif "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/leaderboard")
else:
SWLogger.info("SilentWolf get scores by player success")
if "top_scores" in response:
player_scores = response.top_scores
SWLogger.debug("scores: " + str(scores))
var ld_name = response.ld_name
#print("ld_name: " + str(ld_name))
var ld_config = response.ld_config
var player_name = response.player_name
#print("latest_scores: " + str(leaderboards))
emit_signal("sw_player_scores_received", player_scores)
func _on_GetTopScoreByPlayer_request_completed(result, response_code, headers, body):
SWLogger.info("GetTopScoreByPlayer request completed")
var status_check = CommonErrors.check_status_code(response_code)
SilentWolf.free_request(wrTopScoreByPlayer, TopScoreByPlayer)
SWLogger.debug("response code: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
print("json: " + str(json))
var response = json.result
if response == null:
SWLogger.error("No data returned in GetTopScoreByPlayer response. There was a problem getting the response from the backend.")
emit_signal("sw_player_scores_received", 'No Leaderboard found', scores)
elif "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/leaderboard")
else:
SWLogger.info("SilentWolf get top score by player success")
if "top_score" in response:
print("top score from response: " + str(response.top_score))
if response.top_score == {}:
player_top_score = {}
else:
player_top_score = response.top_score
SWLogger.debug("top score: " + str(player_top_score))
var ld_name = response.ld_name
#print("ld_name: " + str(ld_name))
var ld_config = response.ld_config
var player_name = response.player_name
#print("latest_scores: " + str(leaderboards))
emit_signal("sw_top_player_score_received", player_top_score)
func _on_GetHighScores_request_completed(result, response_code, headers, body):
SWLogger.info("GetHighScores request completed")
var status_check = CommonErrors.check_status_code(response_code)
#print("client status: " + str(HighScores.get_http_client_status()))
#HighScores.queue_free()
SilentWolf.free_request(wrHighScores, HighScores)
SWLogger.debug("response code: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
if response == null:
SWLogger.error("No data returned in GetHighScores response. Leaderboard may be empty")
emit_signal("sw_scores_received", 'No Leaderboard found', scores)
emit_signal("scores_received", scores)
elif "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/leaderboard")
else:
SWLogger.info("SilentWolf get high score success")
if "top_scores" in response:
scores = response.top_scores
SWLogger.debug("scores: " + str(scores))
var ld_name = response.ld_name
#print("ld_name: " + str(ld_name))
var ld_config = response.ld_config
#print("ld_config: " + str(ld_config))
if "period_offset" in response:
var period_offset = str(response["period_offset"])
leaderboards_past_periods[ld_name + ";" + period_offset] = scores
else:
leaderboards[ld_name] = scores
ldboard_config[ld_name] = ld_config
#print("latest_scores: " + str(leaderboards))
emit_signal("sw_scores_received", ld_name, scores)
emit_signal("scores_received", scores)
#var retries = 0
#request_timer.stop()
func _on_DeleteScore_request_completed(result, response_code, headers, body):
SWLogger.info("DeleteScore request completed")
var status_check = CommonErrors.check_status_code(response_code)
SilentWolf.free_request(wrDeleteScore, DeleteScore)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/leaderboard")
else:
SWLogger.info("SilentWolf delete score success")
emit_signal("sw_score_deleted")
func _on_PostNewScore_request_completed(result, response_code, headers, body):
SWLogger.info("PostNewScore request completed")
var status_check = CommonErrors.check_status_code(response_code)
#PostScore.queue_free()
SilentWolf.free_request(wrPostScore, PostScore)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/leaderboard")
else:
SWLogger.info("SilentWolf post score success: " + str(response_code))
if "score_id" in response:
emit_signal("sw_score_posted", response["score_id"])
else:
emit_signal("sw_score_posted")
emit_signal("score_posted")
func _on_GetScorePosition_request_completed(result, response_code, headers, body):
SWLogger.info("GetScorePosition request completed")
var status_check = CommonErrors.check_status_code(response_code)
#ScorePosition.queue_free()
SilentWolf.free_request(wrScorePosition, ScorePosition)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/leaderboard")
else:
SWLogger.info("SilentWolf find score position success.")
position = int(response.position)
emit_signal("sw_position_received", position)
emit_signal("position_received", position)
func _on_ScoresAround_request_completed(result, response_code, headers, body):
SWLogger.info("ScoresAround request completed")
var status_check = CommonErrors.check_status_code(response_code)
SilentWolf.free_request(wrScoresAround, ScoresAround)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/leaderboard")
else:
SWLogger.info("SilentWolf get scores around success")
if "scores_above" in response:
scores_above = response.scores_above
scores_below = response.scores_below
var ld_name = response.ld_name
#print("ld_name: " + str(ld_name))
var ld_config = response.ld_config
#print("ld_config: " + str(ld_config))
ldboard_config[ld_name] = ld_config
if "score_position" in response:
position = response.score_position
emit_signal("sw_scores_around_received", scores_above, scores_below, position)
func _on_WipeLeaderboard_request_completed(result, response_code, headers, body):
SWLogger.info("WipeLeaderboard request completed")
var status_check = CommonErrors.check_status_code(response_code)
#WipeLeaderboard.queue_free()
SilentWolf.free_request(wrWipeLeaderboard, WipeLeaderboard)
SWLogger.debug("response headers: " + str(response_code))
SWLogger.debug("response headers: " + str(headers))
SWLogger.debug("response body: " + str(body.get_string_from_utf8()))
if status_check:
var json = JSON.parse(body.get_string_from_utf8())
var response = json.result
if "message" in response.keys() and response.message == "Forbidden":
SWLogger.error("You are not authorized to call the SilentWolf API - check your API key configuration: https://silentwolf.com/leaderboard")
else:
SWLogger.info("SilentWolf wipe leaderboard success.")
emit_signal("sw_leaderboard_wiped")
func send_get_request(http_node, request_url):
var headers = ["x-api-key: " + SilentWolf.config.api_key, "x-sw-plugin-version: " + SilentWolf.version]
if !http_node.is_inside_tree():
yield(get_tree().create_timer(0.01), "timeout")
SWLogger.debug("Method: GET")
SWLogger.debug("request_url: " + str(request_url))
SWLogger.debug("headers: " + str(headers))
http_node.request(request_url, headers)
func send_post_request(http_node, request_url, payload):
var headers = [
"Content-Type: application/json",
"x-api-key: " + SilentWolf.config.api_key,
"x-sw-plugin-version: " + SilentWolf.version
]
if "post_new_score" in request_url:
SWLogger.info("We're doing a post score")
var player_name = payload["player_name"]
var player_score = payload["score"]
var timestamp = OS.get_system_time_msecs()
var to_be_hashed = [player_name, player_score, timestamp]
SWLogger.debug("send_post_request to_be_hashed: " + str(to_be_hashed))
var hashed = SWHashing.hash_values(to_be_hashed)
SWLogger.debug("send_post_request hashed: " + str(hashed))
headers.append("x-sw-act-tmst: " + str(timestamp))
headers.append("x-sw-act-dig: " + hashed)
var use_ssl = true
if !http_node.is_inside_tree():
yield(get_tree().create_timer(0.01), "timeout")
var query = JSON.print(payload)
SWLogger.info("Method: POST")
SWLogger.info("request_url: " + str(request_url))
SWLogger.info("headers: " + str(headers))
SWLogger.info("query: " + str(query))
http_node.request(request_url, headers, use_ssl, HTTPClient.METHOD_POST, query)

Binary file not shown.

View file

@ -0,0 +1,178 @@
extends Node
const version = "0.6.20"
var godot_version = Engine.get_version_info().string
const SWHashing = preload("res://addons/silent_wolf/utils/SWHashing.gd")
onready var Auth = Node.new()
onready var Scores = Node.new()
onready var Players = Node.new()
onready var Multiplayer = Node.new()
#
# SILENTWOLF CONFIG: THE CONFIG VARIABLES BELOW WILL BE OVERRIDED THE
# NEXT TIME YOU UPDATE YOUR PLUGIN!
#
# As a best practice, use SilentWolf.configure from your game's
# code instead to set the SilentWolf configuration.
#
# See https://silentwolf.com for more details
#
var config = {
"api_key": "FmKF4gtm0Z2RbUAEU62kZ2OZoYLj4PYOURAPIKEY",
"game_id": "YOURGAMEID",
"game_version": "0.0.0",
"log_level": 0
}
var scores_config = {
"open_scene_on_close": "res://scenes/Splash.tscn"
}
var auth_config = {
"redirect_to_scene": "res://scenes/Splash.tscn",
"login_scene": "res://addons/silent_wolf/Auth/Login.tscn",
"email_confirmation_scene": "res://addons/silent_wolf/Auth/ConfirmEmail.tscn",
"reset_password_scene": "res://addons/silent_wolf/Auth/ResetPassword.tscn",
"session_duration_seconds": 0,
"saved_session_expiration_days": 30
}
var SWLogger = load("res://addons/silent_wolf/utils/SWLogger.gd")
var auth_script = load("res://addons/silent_wolf/Auth/Auth.gd")
var scores_script = load("res://addons/silent_wolf/Scores/Scores.gd")
var players_script = load("res://addons/silent_wolf/Players/Players.gd")
var multiplayer_script = load("res://addons/silent_wolf/Multiplayer/Multiplayer.gd")
func _init():
print("SW Init timestamp: " + str(OS.get_time()))
func _ready():
# The following line would keep SilentWolf working even if the game tree is paused.
#pause_mode = Node.PAUSE_MODE_PROCESS
print("SW ready start timestamp: " + str(OS.get_time()))
Auth.set_script(auth_script)
add_child(Auth)
Scores.set_script(scores_script)
add_child(Scores)
Players.set_script(players_script)
add_child(Players)
Multiplayer.set_script(multiplayer_script)
add_child(Multiplayer)
print("SW ready end timestamp: " + str(OS.get_time()))
func configure(json_config):
config = json_config
func configure_api_key(api_key):
config.apiKey = api_key
func configure_game_id(game_id):
config.game_id = game_id
func configure_game_version(game_version):
config.game_version = game_version
##################################################################
# Log levels:
# 0 - error (only log errors)
# 1 - info (log errors and the main actions taken by the SilentWolf plugin) - default setting
# 2 - debug (detailed logs, including the above and much more, to be used when investigating a problem). This shouldn't be the default setting in production.
##################################################################
func configure_log_level(log_level):
config.log_level = log_level
func configure_scores(json_scores_config):
scores_config = json_scores_config
func configure_scores_open_scene_on_close(scene):
scores_config.open_scene_on_close = scene
func configure_auth(json_auth_config):
auth_config = json_auth_config
func configure_auth_redirect_to_scene(scene):
auth_config.open_scene_on_close = scene
func configure_auth_session_duration(duration):
auth_config.session_duration = duration
func free_request(weak_ref, object):
if (weak_ref.get_ref()):
object.queue_free()
func send_get_request(http_node, request_url):
var headers = [
"x-api-key: " + SilentWolf.config.api_key,
"x-sw-game-id: " + SilentWolf.config.game_id,
"x-sw-plugin-version: " + SilentWolf.version,
"x-sw-godot-version: " + godot_version
]
if !http_node.is_inside_tree():
yield(get_tree().create_timer(0.01), "timeout")
SWLogger.debug("Method: GET")
SWLogger.debug("request_url: " + str(request_url))
SWLogger.debug("headers: " + str(headers))
http_node.request(request_url, headers)
func send_post_request(http_node, request_url, payload):
var headers = [
"Content-Type: application/json",
"x-api-key: " + SilentWolf.config.api_key,
"x-sw-game-id: " + SilentWolf.config.game_id,
"x-sw-plugin-version: " + SilentWolf.version,
"x-sw-godot-version: " + godot_version
]
# TODO: this os specific to post_new_score - should be made generic
# or make a section for each type of post request with inetgrity check
# (e.g. also push player data)
if "post_new_score" in request_url:
SWLogger.info("We're doing a post score")
var player_name = payload["player_name"]
var player_score = payload["score"]
var timestamp = OS.get_system_time_msecs()
var to_be_hashed = [player_name, player_score, timestamp]
SWLogger.debug("send_post_request to_be_hashed: " + str(to_be_hashed))
var hashed = SWHashing.hash_values(to_be_hashed)
SWLogger.debug("send_post_request hashed: " + str(hashed))
headers.append("x-sw-act-tmst: " + str(timestamp))
headers.append("x-sw-act-dig: " + hashed)
var use_ssl = true
if !http_node.is_inside_tree():
yield(get_tree().create_timer(0.01), "timeout")
var query = JSON.print(payload)
SWLogger.info("Method: POST")
SWLogger.info("request_url: " + str(request_url))
SWLogger.info("headers: " + str(headers))
SWLogger.info("query: " + str(query))
http_node.request(request_url, headers, use_ssl, HTTPClient.METHOD_POST, query)
func check_auth_ready():
if !Auth:
yield(get_tree().create_timer(0.01), "timeout")
func check_scores_ready():
if !Scores:
yield(get_tree().create_timer(0.01), "timeout")
func check_players_ready():
if !Players:
yield(get_tree().create_timer(0.01), "timeout")
func check_multiplayer_ready():
if !Multiplayer:
yield(get_tree().create_timer(0.01), "timeout")
func check_sw_ready():
if !Auth or !Scores or !Players or !Multiplayer:
yield(get_tree().create_timer(0.01), "timeout")

BIN
addons/silent_wolf/assets/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
addons/silent_wolf/assets/gfx/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 813 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -0,0 +1,17 @@
extends Node
const SWLogger = preload("res://addons/silent_wolf/utils/SWLogger.gd")
func _ready():
pass
static func check_status_code(status_code):
SWLogger.debug("status_code: " + str(status_code))
var check_ok = true
if status_code == 0:
no_connection_error()
check_ok = false
return check_ok
static func no_connection_error():
SWLogger.error("Godot couldn't connect to the SilentWolf backend. There are several reasons why this might happen. See https://silentwolf.com/troubleshooting for more details. If the problem persists you can reach out to us at support@silentwolf.com")

View file

@ -0,0 +1,49 @@
[gd_scene load_steps=6 format=2]
[ext_resource path="res://addons/silent_wolf/assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=1]
[sub_resource type="StyleBoxFlat" id=1]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=2]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=3]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.666667, 0.223529, 0.223529, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="DynamicFont" id=4]
size = 64
font_data = ExtResource( 1 )
[node name="SWButton" type="Button"]
margin_right = 12.0
margin_bottom = 20.0
custom_styles/hover = SubResource( 1 )
custom_styles/pressed = SubResource( 2 )
custom_styles/normal = SubResource( 3 )
custom_fonts/font = SubResource( 4 )
text = "Sample text"

BIN
addons/silent_wolf/examples/.DS_Store vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,87 @@
tool
extends Node2D
const ScoreItem = preload("res://addons/silent_wolf/Scores/ScoreItem.tscn")
const SWLogger = preload("res://addons/silent_wolf/utils/SWLogger.gd")
var list_index = 0
var ld_name = "main"
func _ready():
var scores = []
if ld_name in SilentWolf.Scores.leaderboards:
scores = SilentWolf.Scores.leaderboards[ld_name]
if len(scores) > 0:
render_board(scores)
else:
# use a signal to notify when the high scores have been returned, and show a "loading" animation until it's the case...
add_loading_scores_message()
yield(SilentWolf.Scores.get_high_scores(0), "sw_scores_received")
hide_message()
render_board(SilentWolf.Scores.scores)
func render_board(scores):
if !scores:
add_no_scores_message()
else:
if len(scores) > 1 and scores[0].score > scores[-1].score:
scores.invert()
for i in range(len(scores)):
var score = scores[i]
add_item(score.player_name, str(int(score.score)))
#var time = display_time(scores[i].score)
#add_item(score.player_name, time)
#func display_time(time_in_millis):
# var minutes = int(floor(time_in_millis / 60000))
# var seconds = int(floor((time_in_millis % 60000) / 1000))
# var millis = time_in_millis - minutes*60000 - seconds*1000
# var displayable_time = str(minutes) + ":" + str(seconds) + ":" + str(millis)
# return displayable_time
func reverse_order(scores):
var reverted_scores = scores
if len(scores) > 1 and scores[0].score > scores[-1].score:
reverted_scores = scores.invert()
return reverted_scores
func sort_by_score(a, b):
if a.score > b.score:
return true;
else:
if a.score < b.score:
return false;
else:
return true;
func add_item(player_name, score):
var item = ScoreItem.instance()
list_index += 1
item.get_node("PlayerName").text = str(list_index) + str(". ") + player_name
item.get_node("Score").text = score
item.margin_top = list_index * 100
$"Board/HighScores/ScoreItemContainer".add_child(item)
func add_no_scores_message():
var item = $"Board/MessageContainer/TextMessage"
item.text = "No scores yet!"
$"Board/MessageContainer".show()
item.margin_top = 135
func add_loading_scores_message():
var item = $"Board/MessageContainer/TextMessage"
item.text = "Loading scores..."
$"Board/MessageContainer".show()
item.margin_top = 135
func hide_message():
$"Board/MessageContainer".hide()
func _on_CloseButton_pressed():
var scene_name = SilentWolf.scores_config.open_scene_on_close
print("scene_name: " + str(scene_name))
get_tree().change_scene(scene_name)

View file

@ -0,0 +1,119 @@
[gd_scene load_steps=12 format=2]
[ext_resource path="res://addons/silent_wolf/assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=2]
[ext_resource path="res://addons/silent_wolf/Scores/assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=3]
[ext_resource path="res://addons/silent_wolf/common/SWButton.tscn" type="PackedScene" id=4]
[ext_resource path="res://addons/silent_wolf/examples/CustomLeaderboards/ReverseLeaderboard.gd" type="Script" id=5]
[sub_resource type="DynamicFont" id=1]
size = 76
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=7]
size = 32
font_data = ExtResource( 2 )
[sub_resource type="Theme" id=2]
[sub_resource type="StyleBoxFlat" id=3]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=4]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=5]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.666667, 0.223529, 0.223529, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="DynamicFont" id=6]
size = 64
font_data = ExtResource( 2 )
[node name="ReverseLeaderboard" type="Node2D"]
script = ExtResource( 5 )
[node name="Board" type="VBoxContainer" parent="."]
margin_left = 20.0
margin_top = 20.0
margin_right = 1884.0
margin_bottom = 1071.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="TitleContainer" type="CenterContainer" parent="Board"]
margin_right = 1864.0
margin_bottom = 85.0
[node name="Label" type="Label" parent="Board/TitleContainer"]
margin_left = 502.0
margin_right = 1362.0
margin_bottom = 85.0
custom_fonts/font = SubResource( 1 )
text = "Reverse Leaderboard"
[node name="MessageContainer" type="CenterContainer" parent="Board"]
visible = false
margin_top = 89.0
margin_right = 1864.0
margin_bottom = 126.0
[node name="TextMessage" type="Label" parent="Board/MessageContainer"]
margin_left = 789.0
margin_right = 1075.0
margin_bottom = 37.0
custom_fonts/font = SubResource( 7 )
text = "Loading scores..."
valign = 1
[node name="HighScores" type="CenterContainer" parent="Board"]
margin_top = 89.0
margin_right = 1864.0
margin_bottom = 189.0
rect_min_size = Vector2( 0, 100 )
theme = SubResource( 2 )
[node name="ScoreItemContainer" type="VBoxContainer" parent="Board/HighScores"]
margin_left = 932.0
margin_top = 50.0
margin_right = 932.0
margin_bottom = 50.0
[node name="CloseButtonContainer" type="CenterContainer" parent="Board"]
margin_top = 193.0
margin_right = 1864.0
margin_bottom = 311.0
[node name="CloseButton" parent="Board/CloseButtonContainer" instance=ExtResource( 4 )]
margin_left = 582.0
margin_right = 1281.0
margin_bottom = 118.0
custom_styles/hover = SubResource( 3 )
custom_styles/pressed = SubResource( 4 )
custom_styles/normal = SubResource( 5 )
custom_fonts/font = SubResource( 6 )
text = "Close Leaderboard"
[connection signal="pressed" from="Board/CloseButtonContainer/CloseButton" to="." method="_on_CloseButton_pressed"]

View file

@ -0,0 +1,14 @@
[gd_scene load_steps=3 format=2]
[ext_resource path="res://addons/silent_wolf/Scores/ScoreItem.tscn" type="PackedScene" id=1]
[sub_resource type="StyleBoxFlat" id=1]
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
[node name="ScoreItem" index="0" instance=ExtResource( 1 )]
rect_min_size = Vector2( 434, 64 )
custom_styles/panel = SubResource( 1 )
[node name="Score" parent="." index="1"]
margin_left = 360.0
margin_right = 427.0

View file

@ -0,0 +1,81 @@
extends Node2D
const ScoreItem = preload("SmallScoreItem.tscn")
const SWLogger = preload("res://addons/silent_wolf/utils/SWLogger.gd")
var ld_names = ["Weekly", "Monthly", "main"]
func _ready():
SilentWolf.Scores.connect("sw_scores_received", self, "_on_scores_received")
#var scores = SilentWolf.Scores.scores
add_loading_scores_message()
SilentWolf.Scores.get_high_scores(10, "main")
# the other leaderboard scores will be called once the main call in finished
# (see signal connected above and _on_scores_received function below)
# when all the scores are loaded the leaderboard scene can be opened
func render_boards(leaderboards):
#print("leaderboards: " + str(leaderboards))
var board_number = 0
for board in leaderboards:
var list_index = 1
#print("ld name: " + str(ld_names[board_number]))
#print("ld scores: " + str(board))
for score in board:
add_item(ld_names[board_number], score.player_name, str(int(score.score)), list_index)
list_index += 1
board_number += 1
func add_item(ld_name, player_name, score, list_index):
var item = ScoreItem.instance()
item.get_node("PlayerName").text = str(list_index) + str(". ") + player_name
item.get_node("Score").text = score
item.margin_top = list_index * 100
get_node("MainContainer/Boards/" + ld_name + "/HighScores/ScoreItemContainer").add_child(item)
func add_no_scores_message():
var item = $"MainContainer/MessageContainer/TextMessage"
item.text = "No scores yet!"
$"MainContainer/MessageContainer".show()
item.margin_top = 135
func add_loading_scores_message():
var item = $"MainContainer/MessageContainer/TextMessage"
item.text = "Loading scores..."
$"MainContainer/MessageContainer".show()
item.margin_top = 135
func hide_message():
$"MainContainer/MessageContainer".hide()
func _on_CloseButton_pressed():
var scene_name = SilentWolf.scores_config.open_scene_on_close
SWLogger.info("Closing SilentWolf leaderboard, switching to scene: " + str(scene_name))
#global.reset()
get_tree().change_scene(scene_name)
func _on_scores_received(ld_name, scores):
if ld_name == "main":
SilentWolf.Scores.get_high_scores(10, "Weekly")
#SilentWolf.Scores.get_high_scores(10, "Weekly", -1)
elif ld_name == "Weekly":
SilentWolf.Scores.get_high_scores(10, "Monthly")
else:
#print("SilentWolf.Scores.leaderboards: " + str(SilentWolf.Scores.leaderboards))
var ld_scores = []
for i in [0, 1, 2]:
if ld_names[i] in SilentWolf.Scores.leaderboards:
ld_scores.append(SilentWolf.Scores.leaderboards[ld_names[i]])
#elif (ld_names[i] + ";-1") in SilentWolf.Scores.leaderboards_past_periods:
# ld_scores.append(SilentWolf.Scores.leaderboards_past_periods[(ld_names[i] + ";-1")])
else:
ld_scores.append([])
hide_message()
render_boards(ld_scores)

View file

@ -0,0 +1,228 @@
[gd_scene load_steps=13 format=2]
[ext_resource path="res://addons/silent_wolf/assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=2]
[ext_resource path="res://addons/silent_wolf/Scores/assets/fonts/Comfortaa-Bold.ttf" type="DynamicFontData" id=3]
[ext_resource path="res://addons/silent_wolf/common/SWButton.tscn" type="PackedScene" id=4]
[ext_resource path="res://addons/silent_wolf/examples/CustomLeaderboards/TimeBasedLboards.gd" type="Script" id=5]
[sub_resource type="DynamicFont" id=1]
size = 120
font_data = ExtResource( 3 )
[sub_resource type="DynamicFont" id=2]
size = 76
font_data = ExtResource( 3 )
[sub_resource type="Theme" id=3]
[sub_resource type="DynamicFont" id=8]
size = 32
font_data = ExtResource( 2 )
[sub_resource type="StyleBoxFlat" id=4]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=5]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.831373, 0.415686, 0.415686, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="StyleBoxFlat" id=6]
content_margin_left = 23.0
content_margin_right = 23.0
content_margin_top = 23.0
content_margin_bottom = 23.0
bg_color = Color( 0.666667, 0.223529, 0.223529, 1 )
corner_radius_top_left = 20
corner_radius_top_right = 20
corner_radius_bottom_right = 20
corner_radius_bottom_left = 20
[sub_resource type="DynamicFont" id=7]
size = 64
font_data = ExtResource( 2 )
[node name="TimeBasedLBoards" type="Node2D"]
script = ExtResource( 5 )
[node name="MainContainer" type="VBoxContainer" parent="."]
margin_right = 1918.0
margin_bottom = 1078.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="TitleContainer" type="CenterContainer" parent="MainContainer"]
margin_right = 1918.0
margin_bottom = 135.0
[node name="Label2" type="Label" parent="MainContainer/TitleContainer"]
margin_left = 540.0
margin_right = 1378.0
margin_bottom = 135.0
custom_fonts/font = SubResource( 1 )
text = "Leaderboard"
[node name="Boards" type="HBoxContainer" parent="MainContainer"]
margin_top = 139.0
margin_right = 1918.0
margin_bottom = 328.0
custom_constants/separation = 160
__meta__ = {
"_edit_use_anchors_": false
}
[node name="Weekly" type="VBoxContainer" parent="MainContainer/Boards"]
margin_right = 532.0
margin_bottom = 189.0
size_flags_horizontal = 3
__meta__ = {
"_edit_use_anchors_": false
}
[node name="TitleContainer" type="CenterContainer" parent="MainContainer/Boards/Weekly"]
margin_right = 532.0
margin_bottom = 85.0
[node name="Label" type="Label" parent="MainContainer/Boards/Weekly/TitleContainer"]
margin_left = 76.0
margin_right = 456.0
margin_bottom = 85.0
size_flags_horizontal = 4
custom_fonts/font = SubResource( 2 )
text = "This week"
[node name="HighScores" type="CenterContainer" parent="MainContainer/Boards/Weekly"]
margin_left = 266.0
margin_top = 89.0
margin_right = 266.0
margin_bottom = 189.0
rect_min_size = Vector2( 0, 100 )
size_flags_horizontal = 4
theme = SubResource( 3 )
[node name="ScoreItemContainer" type="VBoxContainer" parent="MainContainer/Boards/Weekly/HighScores"]
margin_top = 50.0
margin_bottom = 50.0
[node name="Monthly" type="VBoxContainer" parent="MainContainer/Boards"]
margin_left = 692.0
margin_right = 1224.0
margin_bottom = 189.0
size_flags_horizontal = 3
__meta__ = {
"_edit_use_anchors_": false
}
[node name="TitleContainer" type="CenterContainer" parent="MainContainer/Boards/Monthly"]
margin_right = 532.0
margin_bottom = 85.0
[node name="Label" type="Label" parent="MainContainer/Boards/Monthly/TitleContainer"]
margin_left = 44.0
margin_right = 488.0
margin_bottom = 85.0
custom_fonts/font = SubResource( 2 )
text = "This month"
[node name="HighScores" type="CenterContainer" parent="MainContainer/Boards/Monthly"]
margin_left = 266.0
margin_top = 89.0
margin_right = 266.0
margin_bottom = 189.0
rect_min_size = Vector2( 0, 100 )
size_flags_horizontal = 4
theme = SubResource( 3 )
[node name="ScoreItemContainer" type="VBoxContainer" parent="MainContainer/Boards/Monthly/HighScores"]
margin_top = 50.0
margin_bottom = 50.0
[node name="main" type="VBoxContainer" parent="MainContainer/Boards"]
margin_left = 1384.0
margin_right = 1918.0
margin_bottom = 189.0
size_flags_horizontal = 3
__meta__ = {
"_edit_use_anchors_": false
}
[node name="TitleContainer" type="CenterContainer" parent="MainContainer/Boards/main"]
margin_right = 534.0
margin_bottom = 85.0
[node name="Label" type="Label" parent="MainContainer/Boards/main/TitleContainer"]
margin_left = 121.0
margin_right = 413.0
margin_bottom = 85.0
custom_fonts/font = SubResource( 2 )
text = "All time"
align = 1
[node name="HighScores" type="CenterContainer" parent="MainContainer/Boards/main"]
margin_left = 267.0
margin_top = 89.0
margin_right = 267.0
margin_bottom = 189.0
rect_min_size = Vector2( 0, 100 )
size_flags_horizontal = 4
theme = SubResource( 3 )
[node name="ScoreItemContainer" type="VBoxContainer" parent="MainContainer/Boards/main/HighScores"]
margin_top = 50.0
margin_bottom = 50.0
[node name="MessageContainer" type="CenterContainer" parent="MainContainer"]
visible = false
margin_left = 1144.0
margin_top = 228.0
margin_right = 3008.0
margin_bottom = 265.0
[node name="TextMessage" type="Label" parent="MainContainer/MessageContainer"]
margin_left = 789.0
margin_right = 1075.0
margin_bottom = 37.0
custom_fonts/font = SubResource( 8 )
text = "Loading scores..."
valign = 1
[node name="CenterContainer" type="CenterContainer" parent="MainContainer"]
margin_top = 332.0
margin_right = 1918.0
margin_bottom = 450.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="CloseButtonContainer" type="CenterContainer" parent="MainContainer/CenterContainer"]
margin_left = 609.0
margin_right = 1308.0
margin_bottom = 118.0
__meta__ = {
"_edit_use_anchors_": false
}
[node name="CloseButton" parent="MainContainer/CenterContainer/CloseButtonContainer" instance=ExtResource( 4 )]
margin_right = 699.0
margin_bottom = 118.0
custom_styles/hover = SubResource( 4 )
custom_styles/pressed = SubResource( 5 )
custom_styles/normal = SubResource( 6 )
custom_fonts/font = SubResource( 7 )
text = "Close Leaderboard"
[connection signal="pressed" from="MainContainer/CenterContainer/CloseButtonContainer/CloseButton" to="." method="_on_CloseButton_pressed"]

View file

@ -0,0 +1,7 @@
[plugin]
name="Silent Wolf plugin"
description="Backend services for Godot Engine."
author="Brass Harpooner"
version="0.6.6"
script="silent_wolf.gd"

View file

@ -0,0 +1,8 @@
tool
extends EditorPlugin
func _enter_tree():
add_autoload_singleton("SilentWolf", "res://addons/silent_wolf/SilentWolf.gd")
func _exit_tree():
remove_autoload_singleton("SilentWolf")

View file

@ -0,0 +1,9 @@
extends Node
static func hash_values(values: Array) -> String:
var to_be_hashed = ""
for value in values:
to_be_hashed = to_be_hashed + str(value)
var hashed = to_be_hashed.md5_text()
#print("Computed hashed: " + str(hashed))
return hashed

View file

@ -0,0 +1,45 @@
extends Node
const SWLogger = preload("res://addons/silent_wolf/utils/SWLogger.gd")
# Retrieves data stored as JSON in local storage
# example path: "user://swsession.save"
# store lookup (not logged in player name) and validator in local file
static func save_data(path: String, data: Dictionary, debug_message: String='Saving data to file in local storage: ') -> void:
var local_file = File.new()
local_file.open(path, File.WRITE)
SWLogger.debug(debug_message + str(data))
local_file.store_line(to_json(data))
local_file.close()
static func remove_data(path: String, debug_message: String='Removing data from file in local storage: ') -> void:
var local_file = File.new()
local_file.open(path, File.WRITE)
var data = {}
SWLogger.debug(debug_message + str(data))
local_file.store_line(to_json(data))
local_file.close()
static func does_file_exist(path: String, file: File=null) -> bool:
var local_file = file
if local_file == null:
local_file = File.new()
return local_file.file_exists(path)
static func get_data(path: String) -> Dictionary:
var local_file = File.new()
var return_data = null
if does_file_exist(path, local_file):
local_file.open(path, File.READ)
var data = parse_json(local_file.get_as_text())
if typeof(data) == TYPE_DICTIONARY:
return_data = data
else:
SWLogger.debug("Invalid data in local storage")
else:
SWLogger.debug("Could not find any data at: " + str(path))
return return_data

View file

@ -0,0 +1,21 @@
extends Node
static func get_log_level():
var log_level = 1
if SilentWolf.config.has('log_level'):
log_level = SilentWolf.config.log_level
else:
error("Couldn't find SilentWolf.config.log_level, defaulting to 1")
return log_level
static func error(text):
printerr(str(text))
push_error(str(text))
static func info(text):
if get_log_level() > 0:
print(str(text))
static func debug(text):
if get_log_level() > 1:
print(str(text))

View file

@ -0,0 +1,47 @@
static func getRandomInt(max_value):
randomize()
return randi() % max_value
static func randomBytes(n):
var r = []
for index in range(0, n):
r.append(getRandomInt(256))
return r
static func uuidbin():
var b = randomBytes(16)
b[6] = (b[6] & 0x0f) | 0x40
b[8] = (b[8] & 0x3f) | 0x80
return b
static func generate_uuid_v4():
var b = uuidbin()
var low = '%02x%02x%02x%02x' % [b[0], b[1], b[2], b[3]]
var mid = '%02x%02x' % [b[4], b[5]]
var hi = '%02x%02x' % [b[6], b[7]]
var clock = '%02x%02x' % [b[8], b[9]]
var node = '%02x%02x%02x%02x%02x%02x' % [b[10], b[11], b[12], b[13], b[14], b[15]]
return '%s-%s-%s-%s-%s' % [low, mid, hi, clock, node]
# argument must be of type string!
static func is_uuid(test_string):
# if length of string is 36 and contains exactly 4 dashes, it's a UUID
return test_string.length() == 36 and test_string.count("-") == 4
# MIT License
# Copyright (c) 2018 Xavier Sellier
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.