Signup form
This commit is contained in:
parent
a5363ac8dd
commit
089013e985
58
client/addons/com.heroiclabs.nakama/Nakama.gd
Normal file
58
client/addons/com.heroiclabs.nakama/Nakama.gd
Normal file
@ -0,0 +1,58 @@
|
||||
tool
|
||||
extends Node
|
||||
|
||||
# The default host address of the server.
|
||||
const DEFAULT_HOST : String = "127.0.0.1"
|
||||
|
||||
# The default port number of the server.
|
||||
const DEFAULT_PORT : int = 7350
|
||||
|
||||
# The default timeout for the connections.
|
||||
const DEFAULT_TIMEOUT = 3
|
||||
|
||||
# The default protocol scheme for the client connection.
|
||||
const DEFAULT_CLIENT_SCHEME : String = "http"
|
||||
|
||||
# The default protocol scheme for the socket connection.
|
||||
const DEFAULT_SOCKET_SCHEME : String = "ws"
|
||||
|
||||
# The default log level for the Nakama logger.
|
||||
const DEFAULT_LOG_LEVEL = NakamaLogger.LOG_LEVEL.DEBUG
|
||||
|
||||
var _http_adapter = null
|
||||
var logger = NakamaLogger.new()
|
||||
|
||||
func get_client_adapter() -> NakamaHTTPAdapter:
|
||||
if _http_adapter == null:
|
||||
_http_adapter = NakamaHTTPAdapter.new()
|
||||
_http_adapter.logger = logger
|
||||
_http_adapter.name = "NakamaHTTPAdapter"
|
||||
add_child(_http_adapter)
|
||||
return _http_adapter
|
||||
|
||||
func create_socket_adapter() -> NakamaSocketAdapter:
|
||||
var adapter = NakamaSocketAdapter.new()
|
||||
adapter.name = "NakamaWebSocketAdapter"
|
||||
adapter.logger = logger
|
||||
add_child(adapter)
|
||||
return adapter
|
||||
|
||||
func create_client(p_server_key : String,
|
||||
p_host : String = DEFAULT_HOST,
|
||||
p_port : int = DEFAULT_PORT,
|
||||
p_scheme : String = DEFAULT_CLIENT_SCHEME,
|
||||
p_timeout : int = DEFAULT_TIMEOUT,
|
||||
p_log_level : int = DEFAULT_LOG_LEVEL) -> NakamaClient:
|
||||
logger._level = p_log_level
|
||||
return NakamaClient.new(get_client_adapter(), p_server_key, p_scheme, p_host, p_port, p_timeout)
|
||||
|
||||
func create_socket(p_host : String = DEFAULT_HOST,
|
||||
p_port : int = DEFAULT_PORT,
|
||||
p_scheme : String = DEFAULT_SOCKET_SCHEME) -> NakamaSocket:
|
||||
return NakamaSocket.new(create_socket_adapter(), p_host, p_port, p_scheme, true)
|
||||
|
||||
func create_socket_from(p_client : NakamaClient) -> NakamaSocket:
|
||||
var scheme = "ws"
|
||||
if p_client.scheme == "https":
|
||||
scheme = "wss"
|
||||
return NakamaSocket.new(create_socket_adapter(), p_client.host, p_client.port, scheme, true)
|
4350
client/addons/com.heroiclabs.nakama/api/NakamaAPI.gd
Normal file
4350
client/addons/com.heroiclabs.nakama/api/NakamaAPI.gd
Normal file
File diff suppressed because it is too large
Load Diff
576
client/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd
Normal file
576
client/addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd
Normal file
@ -0,0 +1,576 @@
|
||||
extends NakamaAsyncResult
|
||||
|
||||
class_name NakamaRTAPI
|
||||
|
||||
# A chat channel on the server.
|
||||
class Channel extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"id": {"name": "id", "type": TYPE_STRING, "required": true},
|
||||
"presences": {"name": "presences", "type": TYPE_ARRAY, "required": true, "content": "UserPresence"},
|
||||
"self": {"name": "self_presence", "type": "UserPresence", "required": true},
|
||||
"room_name": {"name": "room_name", "type": TYPE_STRING, "required": false},
|
||||
"group_id": {"name": "group_id", "type": TYPE_STRING, "required": false},
|
||||
"user_id_one": {"name": "user_id_one", "type": TYPE_STRING, "required": false},
|
||||
"user_id_two": {"name": "user_id_two", "type": TYPE_STRING, "required": false}
|
||||
}
|
||||
|
||||
# The server-assigned channel ID.
|
||||
var id : String
|
||||
|
||||
# The presences visible on the chat channel.
|
||||
var presences : Array # of objects NakamaUserPresence
|
||||
|
||||
# The presence of the current user. i.e. Your self.
|
||||
var self_presence : NakamaRTAPI.UserPresence
|
||||
|
||||
# The name of the chat room, or an empty string if this message was not sent through a chat room.
|
||||
var room_name : String
|
||||
|
||||
# The ID of the group, or an empty string if this message was not sent through a group channel.
|
||||
var group_id : String
|
||||
|
||||
# The ID of the first DM user, or an empty string if this message was not sent through a DM chat.
|
||||
var user_id_one : String
|
||||
|
||||
# The ID of the second DM user, or an empty string if this message was not sent through a DM chat.
|
||||
var user_id_two : String
|
||||
|
||||
func _init(p_ex = null).(p_ex):
|
||||
pass
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "Channel<id=%s, presences=%s, self=%s, room_name=%s, group_id=%s, user_id_one=%s, user_id_two=%s>" % [
|
||||
id, presences, self_presence, room_name, group_id, user_id_one, user_id_two
|
||||
]
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> Channel:
|
||||
return _safe_ret(NakamaSerializer.deserialize(p_ns, "Channel", p_dict), Channel) as Channel
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "channel"
|
||||
|
||||
|
||||
class ChannelMessageAck extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"channel_id": {"name": "channel_id", "type": TYPE_STRING, "required": true},
|
||||
"code": {"name": "code", "type": TYPE_INT, "required": true},
|
||||
"create_time": {"name": "create_time", "type": TYPE_STRING, "required": false},
|
||||
"message_id": {"name": "message_id", "type": TYPE_STRING, "required": true},
|
||||
"persistent": {"name": "persistent", "type": TYPE_BOOL, "required": false},
|
||||
"update_time": {"name": "update_time", "type": TYPE_STRING, "required": false},
|
||||
"username": {"name": "username", "type": TYPE_STRING, "required": false},
|
||||
"room_name": {"name": "room_name", "type": TYPE_STRING, "required": false},
|
||||
"group_id": {"name": "group_id", "type": TYPE_STRING, "required": false},
|
||||
"user_id_one": {"name": "user_id_one", "type": TYPE_STRING, "required": false},
|
||||
"user_id_two": {"name": "user_id_two", "type": TYPE_STRING, "required": false}
|
||||
}
|
||||
|
||||
# The server-assigned channel ID.
|
||||
var channel_id : String
|
||||
|
||||
# A user-defined code for the chat message.
|
||||
var code : int
|
||||
|
||||
# The UNIX time when the message was created.
|
||||
var create_time : String
|
||||
|
||||
# A unique ID for the chat message.
|
||||
var message_id : String
|
||||
|
||||
# True if the chat message has been stored in history.
|
||||
var persistent : bool
|
||||
|
||||
# The UNIX time when the message was updated.
|
||||
var update_time : String
|
||||
|
||||
# The username of the sender of the message.
|
||||
var username : String
|
||||
|
||||
# The name of the chat room, or an empty string if this message was not sent through a chat room.
|
||||
var room_name : String
|
||||
|
||||
# The ID of the group, or an empty string if this message was not sent through a group channel.
|
||||
var group_id : String
|
||||
|
||||
# The ID of the first DM user, or an empty string if this message was not sent through a DM chat.
|
||||
var user_id_one : String
|
||||
|
||||
# The ID of the second DM user, or an empty string if this message was not sent through a DM chat.
|
||||
var user_id_two : String
|
||||
|
||||
func _init(p_ex = null).(p_ex):
|
||||
pass
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "ChannelMessageAck<channel_id=%s, code=%d, create_time=%s, message_id=%s, persistent=%s, update_time=%s, username=%s room_name=%s, group_id=%s, user_id_one=%s, user_id_two=%s>" % [
|
||||
channel_id, code, create_time, message_id, persistent, update_time, username, room_name, group_id, user_id_one, user_id_two
|
||||
]
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ChannelMessageAck:
|
||||
return _safe_ret(NakamaSerializer.deserialize(p_ns, "ChannelMessageAck", p_dict), ChannelMessageAck) as ChannelMessageAck
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "channel_message_ack"
|
||||
|
||||
|
||||
# A batch of join and leave presences on a chat channel.
|
||||
class ChannelPresenceEvent extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"channel_id": {"name": "channel_id", "type": TYPE_STRING, "required": true},
|
||||
"joins": {"name": "joins", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
|
||||
"leaves": {"name": "leaves", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
|
||||
"room_name": {"name": "room_name", "type": TYPE_STRING, "required": false},
|
||||
"group_id": {"name": "group_id", "type": TYPE_STRING, "required": false},
|
||||
"user_id_one": {"name": "user_id_one", "type": TYPE_STRING, "required": false},
|
||||
"user_id_two": {"name": "user_id_two", "type": TYPE_STRING, "required": false}
|
||||
}
|
||||
|
||||
# The unique identifier of the chat channel.
|
||||
var channel_id : String
|
||||
|
||||
# Presences of the users who joined the channel.
|
||||
var joins : Array # UserPresence
|
||||
|
||||
# Presences of users who left the channel.
|
||||
var leaves : Array # UserPresence
|
||||
|
||||
# The name of the chat room, or an empty string if this message was not sent through a chat room.
|
||||
var room_name : String
|
||||
|
||||
# The ID of the group, or an empty string if this message was not sent through a group channel.
|
||||
var group_id : String
|
||||
|
||||
# The ID of the first DM user, or an empty string if this message was not sent through a DM chat.
|
||||
var user_id_one : String
|
||||
|
||||
# The ID of the second DM user, or an empty string if this message was not sent through a DM chat.
|
||||
var user_id_two : String
|
||||
|
||||
func _init(p_ex = null).(p_ex):
|
||||
pass
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "ChannelPresenceEvent<channel_id=%s, joins=%s, leaves=%s, room_name=%s, group_id=%s, user_id_one=%s, user_id_two=%s>" % [
|
||||
channel_id, joins, leaves, room_name, group_id, user_id_one, user_id_two
|
||||
]
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> ChannelPresenceEvent:
|
||||
return _safe_ret(NakamaSerializer.deserialize(p_ns, "ChannelPresenceEvent", p_dict), ChannelPresenceEvent) as ChannelPresenceEvent
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "channel_presence_event"
|
||||
|
||||
# A multiplayer match.
|
||||
class Match extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"authoritative": {"name": "authoritative", "type": TYPE_BOOL, "required": false},
|
||||
"match_id": {"name": "match_id", "type": TYPE_STRING, "required": true},
|
||||
"label": {"name": "label", "type": TYPE_STRING, "required": false},
|
||||
"presences": {"name": "presences", "type": TYPE_ARRAY, "required": false, "content": "UserPresence"},
|
||||
"size": {"name": "size", "type": TYPE_INT, "required": false},
|
||||
"self": {"name": "self_user", "type": "UserPresence", "required": true}
|
||||
}
|
||||
|
||||
# If this match has an authoritative handler on the server.
|
||||
var authoritative : bool
|
||||
|
||||
# The unique match identifier.
|
||||
var match_id : String
|
||||
|
||||
# A label for the match which can be filtered on.
|
||||
var label : String
|
||||
|
||||
# The presences already in the match.
|
||||
var presences : Array # UserPresence
|
||||
|
||||
# The number of users currently in the match.
|
||||
var size : int
|
||||
|
||||
# The current user in this match. i.e. Yourself.
|
||||
var self_user : UserPresence
|
||||
|
||||
func _init(p_ex = null).(p_ex):
|
||||
pass
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary):
|
||||
return _safe_ret(NakamaSerializer.deserialize(p_ns, "Match", p_dict), Match) as Match
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "Match<authoritative=%s, match_id=%s, label=%s, presences=%s, size=%d, self=%s>" % [authoritative, match_id, label, presences, size, self_user]
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "match"
|
||||
|
||||
|
||||
# Some game state update in a match.
|
||||
class MatchData extends NakamaAsyncResult:
|
||||
const _SCHEMA = {
|
||||
"match_id": {"name": "match_id", "type": TYPE_STRING, "required": true},
|
||||
"presence": {"name": "presence", "type": "UserPresence", "required": false},
|
||||
"op_code": {"name": "op_code", "type": TYPE_STRING, "required": false},
|
||||
"data": {"name": "data", "type": TYPE_STRING, "required": false}
|
||||
}
|
||||
|
||||
# The unique match identifier.
|
||||
var match_id : String
|
||||
|
||||
# The operation code for the state change.
|
||||
# This value can be used to mark the type of the contents of the state.
|
||||
var op_code : int = 0
|
||||
|
||||
# The user that sent this game state update.
|
||||
var presence : UserPresence
|
||||
|
||||
# The byte contents of the state change.
|
||||
var data : String
|
||||
|
||||
func _init(p_ex = null).(p_ex):
|
||||
pass
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "MatchData<match_id=%s, op_code=%s, presence=%s, data=%s>" % [match_id, op_code, presence, data]
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchData:
|
||||
var out := _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchData", p_dict), MatchData) as MatchData
|
||||
if out.data: # Decode base64 received data
|
||||
out.data = Marshalls.base64_to_utf8(out.data)
|
||||
return out
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "match_data"
|
||||
|
||||
|
||||
# A batch of join and leave presences for a match.
|
||||
class MatchPresenceEvent extends NakamaAsyncResult:
|
||||
const _SCHEMA = {
|
||||
"match_id": {"name": "match_id", "type": TYPE_STRING, "required": true},
|
||||
"joins": {"name": "joins", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
|
||||
"leaves": {"name": "leaves", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
|
||||
}
|
||||
|
||||
# Presences of users who joined the match.
|
||||
var joins : Array
|
||||
|
||||
# Presences of users who left the match.
|
||||
var leaves : Array
|
||||
|
||||
# The unique match identifier.
|
||||
var match_id : String
|
||||
|
||||
func _init(p_ex = null).(p_ex):
|
||||
pass
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "MatchPresenceEvent<match_id=%s, joins=%s, leaves=%s>" % [match_id, joins, leaves]
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchPresenceEvent:
|
||||
return _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchPresenceEvent", p_dict), MatchPresenceEvent) as MatchPresenceEvent
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "match_presence_event"
|
||||
|
||||
# The result of a successful matchmaker operation sent to the server.
|
||||
class MatchmakerMatched extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"match_id": {"name": "match_id", "type": TYPE_STRING, "required": false},
|
||||
"ticket": {"name": "ticket", "type": TYPE_STRING, "required": true},
|
||||
"token": {"name": "token", "type": TYPE_STRING, "required": false},
|
||||
"users": {"name": "users", "type": TYPE_ARRAY, "required": false, "content": "MatchmakerUser"},
|
||||
"self": {"name": "self_user", "type": "MatchmakerUser", "required": true}
|
||||
}
|
||||
|
||||
# The id used to join the match.
|
||||
# A match ID used to join the match.
|
||||
var match_id : String
|
||||
|
||||
# The ticket sent by the server when the user requested to matchmake for other players.
|
||||
var ticket : String
|
||||
|
||||
# The token used to join a match.
|
||||
var token : String
|
||||
|
||||
# The other users matched with this user and the parameters they sent.
|
||||
var users : Array # MatchmakerUser
|
||||
|
||||
# The current user who matched with opponents.
|
||||
var self_user : MatchmakerUser
|
||||
|
||||
func _init(p_ex = null).(p_ex):
|
||||
pass
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "<MatchmakerMatched match_id=%s, ticket=%s, token=%s, users=%s, self=%s>" % [
|
||||
match_id, ticket, token, users, self_user
|
||||
]
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchmakerMatched:
|
||||
return _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchmakerMatched", p_dict), MatchmakerMatched) as MatchmakerMatched
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "matchmaker_matched"
|
||||
|
||||
|
||||
# The matchmaker ticket received from the server.
|
||||
class MatchmakerTicket extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"ticket": {"name": "ticket", "type": TYPE_STRING, "required": true}
|
||||
}
|
||||
|
||||
# The ticket generated by the matchmaker.
|
||||
var ticket : String
|
||||
|
||||
func _init(p_ex = null).(p_ex):
|
||||
pass
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchmakerTicket:
|
||||
return _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchmakerTicket", p_dict), MatchmakerTicket) as MatchmakerTicket
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "<MatchmakerTicket ticket=%s>" % ticket
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "matchmaker_ticket"
|
||||
|
||||
|
||||
# The user with the parameters they sent to the server when asking for opponents.
|
||||
class MatchmakerUser extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"numeric_properties": {"name": "numeric_properties", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_REAL},
|
||||
"string_properties": {"name": "string_properties", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
"presence": {"name": "presence", "type": "UserPresence", "required": true}
|
||||
}
|
||||
|
||||
# The numeric properties which this user asked to matchmake with.
|
||||
var numeric_properties : Dictionary
|
||||
|
||||
# The presence of the user.
|
||||
var presence : UserPresence
|
||||
|
||||
# The string properties which this user asked to matchmake with.
|
||||
var string_properties : Dictionary
|
||||
|
||||
func _init(p_ex = null).(p_ex):
|
||||
pass
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "<MatchmakerUser presence=%s, numeric_properties=%s, string_properties=%s>" % [
|
||||
presence, numeric_properties, string_properties]
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> MatchmakerUser:
|
||||
return _safe_ret(NakamaSerializer.deserialize(p_ns, "MatchmakerUser", p_dict), MatchmakerUser) as MatchmakerUser
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "matchmaker_user"
|
||||
|
||||
|
||||
# Receive status updates for users.
|
||||
class Status extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"presences": {"name": "presences", "type": TYPE_ARRAY, "required": true, "content": "UserPresence"},
|
||||
}
|
||||
|
||||
# The status events for the users followed.
|
||||
var presences := Array()
|
||||
|
||||
func _init(p_ex = null).(p_ex):
|
||||
pass
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> Status:
|
||||
return _safe_ret(NakamaSerializer.deserialize(p_ns, "Status", p_dict), Status) as Status
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "<Status presences=%s>" % [presences]
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "status"
|
||||
|
||||
|
||||
# A status update event about other users who've come online or gone offline.
|
||||
class StatusPresenceEvent extends NakamaAsyncResult:
|
||||
const _SCHEMA = {
|
||||
"joins": {"name": "joins", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
|
||||
"leaves": {"name": "leaves", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
|
||||
}
|
||||
|
||||
# Presences of users who joined the server.
|
||||
# This join information is in response to a subscription made to be notified when a user comes online.
|
||||
var joins : Array
|
||||
|
||||
# Presences of users who left the server.
|
||||
# This leave information is in response to a subscription made to be notified when a user goes offline.
|
||||
var leaves : Array
|
||||
|
||||
func _init(p_ex = null).(p_ex):
|
||||
pass
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "StatusPresenceEvent<joins=%s, leaves=%s>" % [joins, leaves]
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> StatusPresenceEvent:
|
||||
return _safe_ret(NakamaSerializer.deserialize(p_ns, "StatusPresenceEvent", p_dict), StatusPresenceEvent) as StatusPresenceEvent
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "status_presence_event"
|
||||
|
||||
|
||||
# A realtime socket stream on the server.
|
||||
class Stream extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"mode": {"name": "mode", "type": TYPE_INT, "required": true},
|
||||
"subject": {"name": "subject", "type": TYPE_STRING, "required": false},
|
||||
"subcontext": {"name": "subcontext", "type": TYPE_STRING, "required": false},
|
||||
"label": {"name": "label", "type": TYPE_STRING, "required": false},
|
||||
}
|
||||
|
||||
# The mode of the stream.
|
||||
var mode : int
|
||||
|
||||
# The subject of the stream. This is usually a user id.
|
||||
var subject : String
|
||||
|
||||
# The descriptor of the stream. Used with direct chat messages and contains a second user id.
|
||||
var subcontext : String
|
||||
|
||||
# Identifies streams which have a context across users like a chat channel room.
|
||||
var label : String
|
||||
|
||||
func _init(p_ex = null).(p_ex):
|
||||
pass
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "Stream<mode=%s, subject=%s, subcontext=%s, label=%s>" % [mode, subject, subcontext, label]
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> Stream:
|
||||
return _safe_ret(NakamaSerializer.deserialize(p_ns, "Stream", p_dict), Stream) as Stream
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "stream"
|
||||
|
||||
|
||||
# A batch of joins and leaves on the low level stream.
|
||||
# Streams are built on to provide abstractions for matches, chat channels, etc. In most cases you'll never need to
|
||||
# interact with the low level stream itself.
|
||||
class StreamPresenceEvent extends NakamaAsyncResult:
|
||||
const _SCHEMA = {
|
||||
"stream": {"name": "stream", "type": "Stream", "required": true},
|
||||
"joins": {"name": "joins", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
|
||||
"leaves": {"name": "leaves", "type": TYPE_ARRAY, "required": false, "content" : "UserPresence"},
|
||||
}
|
||||
|
||||
# Presences of users who left the stream.
|
||||
var joins : Array
|
||||
|
||||
# Presences of users who joined the stream.
|
||||
var leaves : Array
|
||||
|
||||
# The identifier for the stream.
|
||||
var stream : Stream = null
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "StreamPresenceEvent<stream=%s, joins=%s, leaves=%s>" % [stream, joins, leaves]
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> StreamPresenceEvent:
|
||||
return _safe_ret(NakamaSerializer.deserialize(p_ns, "StreamPresenceEvent", p_dict), StreamPresenceEvent) as StreamPresenceEvent
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "stream_presence_event"
|
||||
|
||||
|
||||
# A state change received from a stream.
|
||||
class StreamData extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"stream": {"name": "stream", "type": "Stream", "required": true},
|
||||
"sender": {"name": "sender", "type": "UserPresence", "required": false},
|
||||
"data": {"name": "state", "type": TYPE_STRING, "required": false},
|
||||
"reliable": {"name": "reliable", "type": TYPE_BOOL, "required": false},
|
||||
}
|
||||
|
||||
# The user who sent the state change. May be `null`.
|
||||
var sender : UserPresence = null
|
||||
|
||||
# The contents of the state change.
|
||||
var state : String
|
||||
|
||||
# The identifier for the stream.
|
||||
var stream : Stream
|
||||
|
||||
# True if this data was delivered reliably, false otherwise.
|
||||
var reliable : bool
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "StreamData<sender=%s, state=%s, stream=%s>" % [sender, state, stream]
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> StreamData:
|
||||
return _safe_ret(NakamaSerializer.deserialize(p_ns, "StreamData", p_dict), StreamData) as StreamData
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "stream_data"
|
||||
|
||||
# An object which represents a connected user in the server.
|
||||
# The server allows the same user to be connected with multiple sessions. To uniquely identify them a tuple of
|
||||
# `{ node_id, user_id, session_id }` is used which is exposed as this object.
|
||||
class UserPresence extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"persistence": {"name": "persistence", "type": TYPE_BOOL, "required": false},
|
||||
"session_id": {"name": "session_id", "type": TYPE_STRING, "required": true},
|
||||
"status": {"name": "status", "type": TYPE_STRING, "required": false},
|
||||
"username": {"name": "username", "type": TYPE_STRING, "required": false},
|
||||
"user_id": {"name": "user_id", "type": TYPE_STRING, "required": true},
|
||||
}
|
||||
|
||||
# If this presence generates stored events like persistent chat messages or notifications.
|
||||
var persistence : bool
|
||||
|
||||
# The session id of the user.
|
||||
var session_id : String
|
||||
|
||||
# The status of the user with the presence on the server.
|
||||
var status : String
|
||||
|
||||
# The username for the user.
|
||||
var username : String
|
||||
|
||||
# The id of the user.
|
||||
var user_id : String
|
||||
|
||||
func _init(p_ex = null).(p_ex):
|
||||
pass
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func _to_string():
|
||||
if is_exception(): return get_exception()._to_string()
|
||||
return "UserPresence<persistence=%s, session_id=%s, status=%s, username=%s, user_id=%s>" % [
|
||||
persistence, session_id, status, username, user_id]
|
||||
|
||||
static func create(p_ns : GDScript, p_dict : Dictionary) -> UserPresence:
|
||||
return _safe_ret(NakamaSerializer.deserialize(p_ns, "UserPresence", p_dict), UserPresence) as UserPresence
|
||||
|
||||
static func get_result_key() -> String:
|
||||
return "user_presence"
|
363
client/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd
Normal file
363
client/addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd
Normal file
@ -0,0 +1,363 @@
|
||||
extends Reference
|
||||
class_name NakamaRTMessage
|
||||
|
||||
# Send a channel join message to the server.
|
||||
class ChannelJoin:
|
||||
|
||||
const _SCHEMA = {
|
||||
"persistence": {"name": "persistence", "type": TYPE_BOOL, "required": true},
|
||||
"hidden": {"name": "hidden", "type": TYPE_BOOL, "required": true},
|
||||
"target": {"name": "target", "type": TYPE_STRING, "required": true},
|
||||
"type": {"name": "type", "type": TYPE_INT, "required": true},
|
||||
}
|
||||
|
||||
enum ChannelType {
|
||||
# A chat room which can be created dynamically with a name.
|
||||
Room = 1,
|
||||
# A private chat between two users.
|
||||
DirectMessage = 2,
|
||||
# A chat within a group on the server.
|
||||
Group = 3
|
||||
}
|
||||
|
||||
var persistence : bool
|
||||
var hidden : bool
|
||||
var target : String
|
||||
var type : int
|
||||
|
||||
func _init(p_target : String, p_type : int, p_persistence : bool, p_hidden : bool):
|
||||
persistence = p_persistence
|
||||
hidden = p_hidden
|
||||
target = p_target
|
||||
type = p_type if p_type >= ChannelType.Room and p_type <= ChannelType.Group else 0 # Will cause error server side
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func get_msg_key() -> String:
|
||||
return "channel_join"
|
||||
|
||||
func _to_string():
|
||||
return "ChannelJoin<persistence=%s, hidden=%s, target=%s, type=%d>" % [persistence, hidden, target, type]
|
||||
|
||||
|
||||
# A leave message for a match on the server.
|
||||
class ChannelLeave extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"channel_id": {"name": "channel_id", "type": TYPE_STRING, "required": true}
|
||||
}
|
||||
var channel_id : String
|
||||
|
||||
func _init(p_channel_id : String):
|
||||
channel_id = p_channel_id
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func get_msg_key() -> String:
|
||||
return "channel_leave"
|
||||
|
||||
func _to_string():
|
||||
return "ChannelLeave<channel_id=%s>" % [channel_id]
|
||||
|
||||
|
||||
class ChannelMessageRemove extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"channel_id": {"name": "channel_id", "type": TYPE_STRING, "required": true},
|
||||
"message_id": {"name": "message_id", "type": TYPE_STRING, "required": true}
|
||||
}
|
||||
|
||||
var channel_id : String
|
||||
var message_id : String
|
||||
|
||||
func _init(p_channel_id : String, p_message_id):
|
||||
channel_id = p_channel_id
|
||||
message_id = p_message_id
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func get_msg_key() -> String:
|
||||
return "channel_message_remove"
|
||||
|
||||
func _to_string():
|
||||
return "ChannelMessageRemove<channel_id=%s, message_id=%s>" % [channel_id, message_id]
|
||||
|
||||
|
||||
# Send a chat message to a channel on the server.
|
||||
class ChannelMessageSend extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"channel_id": {"name": "channel_id", "type": TYPE_STRING, "required": true},
|
||||
"content": {"name": "content", "type": TYPE_STRING, "required": true}
|
||||
}
|
||||
|
||||
var channel_id : String
|
||||
var content : String
|
||||
|
||||
func _init(p_channel_id : String, p_content):
|
||||
channel_id = p_channel_id
|
||||
content = p_content
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func get_msg_key() -> String:
|
||||
return "channel_message_send"
|
||||
|
||||
func _to_string():
|
||||
return "ChannelMessageSend<channel_id=%s, content=%s>" % [channel_id, content]
|
||||
|
||||
|
||||
class ChannelMessageUpdate extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"channel_id": {"name": "channel_id", "type": TYPE_STRING, "required": true},
|
||||
"message_id": {"name": "message_id", "type": TYPE_STRING, "required": true},
|
||||
"content": {"name": "content", "type": TYPE_STRING, "required": true}
|
||||
}
|
||||
|
||||
var channel_id : String
|
||||
var message_id : String
|
||||
var content : String
|
||||
|
||||
func _init(p_channel_id : String, p_message_id, p_content : String):
|
||||
channel_id = p_channel_id
|
||||
message_id = p_message_id
|
||||
content = p_content
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func get_msg_key() -> String:
|
||||
return "channel_message_update"
|
||||
|
||||
func _to_string():
|
||||
return "ChannelMessageUpdate<channel_id=%s, message_id=%s, content=%s>" % [channel_id, message_id, content]
|
||||
|
||||
# A create message for a match on the server.
|
||||
class MatchCreate extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {}
|
||||
|
||||
func _init():
|
||||
pass
|
||||
|
||||
func serialize():
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func get_msg_key() -> String:
|
||||
return "match_create"
|
||||
|
||||
func _to_string():
|
||||
return "MatchCreate<>"
|
||||
|
||||
|
||||
# A join message for a match on the server.
|
||||
class MatchJoin extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"match_id": {"name": "match_id", "type": TYPE_STRING, "required": false},
|
||||
"token": {"name": "token", "type": TYPE_STRING, "required": false},
|
||||
"metadata": {"name": "metadata", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
}
|
||||
|
||||
# These two are mutually exclusive and set manually by socket for now, so use null.
|
||||
var match_id = null
|
||||
var token = null
|
||||
var metadata = null
|
||||
|
||||
func _init(p_ex=null).(p_ex):
|
||||
pass
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func get_msg_key() -> String:
|
||||
return "match_join"
|
||||
|
||||
func _to_string():
|
||||
return "MatchJoin<match_id=%s, token=%s, metadata=%s>" % [match_id, token, metadata]
|
||||
|
||||
|
||||
# A leave message for a match on the server.
|
||||
class MatchLeave extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"match_id": {"name": "match_id", "type": TYPE_STRING, "required": true}
|
||||
}
|
||||
var match_id : String
|
||||
|
||||
func _init(p_match_id : String):
|
||||
match_id = p_match_id
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func get_msg_key() -> String:
|
||||
return "match_leave"
|
||||
|
||||
func _to_string():
|
||||
return "MatchLeave<match_id=%s>" % [match_id]
|
||||
|
||||
|
||||
# Send new state to a match on the server.
|
||||
class MatchDataSend extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"match_id": {"name": "match_id", "type": TYPE_STRING, "required": true},
|
||||
"op_code": {"name": "op_code", "type": TYPE_INT, "required": true},
|
||||
"presences": {"name": "presences", "type": TYPE_ARRAY, "required": false, "content": "UserPresences"},
|
||||
"data": {"name": "data", "type": TYPE_STRING, "required": true},
|
||||
}
|
||||
|
||||
var match_id : String
|
||||
var presences = null
|
||||
var op_code : int
|
||||
var data : String
|
||||
|
||||
func _init(p_match_id : String, p_op_code : int, p_data : String, p_presences):
|
||||
match_id = p_match_id
|
||||
presences = p_presences
|
||||
op_code = p_op_code
|
||||
data = p_data
|
||||
|
||||
func serialize():
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func get_msg_key():
|
||||
return "match_data_send"
|
||||
|
||||
func _to_string():
|
||||
return "MatchDataSend<match_id=%s, op_code=%s, presences=%s, data=%s>" % [match_id, op_code, presences, data]
|
||||
|
||||
|
||||
# Add the user to the matchmaker pool with properties.
|
||||
class MatchmakerAdd extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"query": {"name": "query", "type": TYPE_STRING, "required": true},
|
||||
"max_count": {"name": "max_count", "type": TYPE_INT, "required": true},
|
||||
"min_count": {"name": "min_count", "type": TYPE_INT, "required": true},
|
||||
"numeric_properties": {"name": "numeric_properties", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_REAL},
|
||||
"string_properties": {"name": "string_properties", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
}
|
||||
|
||||
var query : String = "*"
|
||||
var max_count : int = 8
|
||||
var min_count : int = 2
|
||||
var string_properties : Dictionary
|
||||
var numeric_properties : Dictionary
|
||||
|
||||
func _no_set(_val):
|
||||
return
|
||||
|
||||
func _init(p_query : String = "*", p_min_count : int = 2, p_max_count : int = 8,
|
||||
p_string_props : Dictionary = Dictionary(), p_numeric_props : Dictionary = Dictionary()):
|
||||
query = p_query
|
||||
min_count = p_min_count
|
||||
max_count = p_max_count
|
||||
string_properties = p_string_props
|
||||
numeric_properties = p_numeric_props
|
||||
|
||||
func serialize() -> Dictionary:
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func get_msg_key() -> String:
|
||||
return "matchmaker_add"
|
||||
|
||||
func _to_string():
|
||||
return "MatchmakerAdd<query=%s, max_count=%d, min_count=%d, numeric_properties=%s, string_properties=%s>" % [query, max_count, min_count, numeric_properties, string_properties]
|
||||
|
||||
|
||||
# Remove the user from the matchmaker pool by ticket.
|
||||
class MatchmakerRemove extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"ticket": {"name": "ticket", "type": TYPE_STRING, "required": true}
|
||||
}
|
||||
|
||||
var ticket : String
|
||||
|
||||
func _init(p_ticket : String):
|
||||
ticket = p_ticket
|
||||
|
||||
func serialize():
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func get_msg_key() -> String:
|
||||
return "matchmaker_remove"
|
||||
|
||||
func _to_string():
|
||||
return "MatchmakerRemove<ticket=%s>" % [ticket]
|
||||
|
||||
|
||||
# Follow one or more other users for status updates.
|
||||
class StatusFollow extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"user_ids": {"name": "user_ids", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
"usernames": {"name": "usernames", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
}
|
||||
|
||||
var user_ids := PoolStringArray()
|
||||
var usernames := PoolStringArray()
|
||||
|
||||
func _init(p_ids : PoolStringArray, p_usernames : PoolStringArray):
|
||||
user_ids = p_ids
|
||||
usernames = p_usernames
|
||||
|
||||
func serialize():
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func get_msg_key() -> String:
|
||||
return "status_follow"
|
||||
|
||||
func _to_string():
|
||||
return "StatusFollow<user_ids=%s, usernames=%s>" % [user_ids, usernames]
|
||||
|
||||
|
||||
# Unfollow one or more users on the server.
|
||||
class StatusUnfollow extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"user_ids": {"name": "user_ids", "type": TYPE_DICTIONARY, "required": false, "content": TYPE_STRING},
|
||||
}
|
||||
|
||||
var user_ids := PoolStringArray()
|
||||
|
||||
func _init(p_ids : PoolStringArray):
|
||||
user_ids = p_ids
|
||||
|
||||
func serialize():
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func get_msg_key() -> String:
|
||||
return "status_unfollow"
|
||||
|
||||
func _to_string():
|
||||
return "StatusUnfollow<user_ids=%s>" % [user_ids]
|
||||
|
||||
|
||||
# Unfollow one or more users on the server.
|
||||
class StatusUpdate extends NakamaAsyncResult:
|
||||
|
||||
const _SCHEMA = {
|
||||
"status": {"name": "status", "type": TYPE_STRING, "required": true},
|
||||
}
|
||||
|
||||
var status : String
|
||||
|
||||
func _init(p_status : String):
|
||||
status = p_status
|
||||
|
||||
func serialize():
|
||||
return NakamaSerializer.serialize(self)
|
||||
|
||||
func get_msg_key() -> String:
|
||||
return "status_update"
|
||||
|
||||
func _to_string():
|
||||
return "StatusUpdate<status=%s>" % [status]
|
64
client/addons/com.heroiclabs.nakama/api/NakamaSession.gd
Normal file
64
client/addons/com.heroiclabs.nakama/api/NakamaSession.gd
Normal file
@ -0,0 +1,64 @@
|
||||
extends NakamaAsyncResult
|
||||
class_name NakamaSession
|
||||
|
||||
|
||||
var created : bool = false setget _no_set
|
||||
var token : String = "" setget _no_set
|
||||
var create_time : int = 0 setget _no_set
|
||||
var expire_time : int = 0 setget _no_set
|
||||
var expired : bool = true setget _no_set, is_expired
|
||||
var vars : Dictionary = {} setget _no_set
|
||||
var username : String = "" setget _no_set
|
||||
var user_id : String = "" setget _no_set
|
||||
var valid : bool = false setget _no_set, is_valid
|
||||
|
||||
func _no_set(v):
|
||||
return
|
||||
|
||||
func is_expired() -> bool:
|
||||
return expire_time < OS.get_unix_time()
|
||||
|
||||
func is_valid():
|
||||
return valid
|
||||
|
||||
func _init(p_token = null, p_created : bool = false, p_exception = null).(p_exception):
|
||||
if p_token:
|
||||
var unpacked = _jwt_unpack(p_token)
|
||||
var decoded = {}
|
||||
if not validate_json(unpacked):
|
||||
decoded = parse_json(unpacked)
|
||||
valid = true
|
||||
if typeof(decoded) != TYPE_DICTIONARY:
|
||||
decoded = {}
|
||||
if decoded.empty():
|
||||
valid = false
|
||||
if p_exception == null:
|
||||
_ex = NakamaException.new("Unable to unpack token")
|
||||
|
||||
token = p_token
|
||||
created = p_created
|
||||
create_time = OS.get_unix_time()
|
||||
expire_time = int(decoded.get("exp", 0))
|
||||
username = str(decoded.get("usn", ""))
|
||||
user_id = str(decoded.get("uid", ""))
|
||||
if decoded.has("vrs") and typeof(decoded["vrs"]) == TYPE_DICTIONARY:
|
||||
for k in decoded["vrs"]:
|
||||
vars[k] = decoded["vrs"][k]
|
||||
|
||||
func _to_string():
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
return "Session<created=%s, token=%s, create_time=%d, username=%s, user_id=%s, vars=%s>" % [
|
||||
created, token, create_time, username, user_id, str(vars)]
|
||||
|
||||
func _jwt_unpack(p_token : String) -> String:
|
||||
# Hack decode JSON payload from JWT.
|
||||
if p_token.find(".") == -1:
|
||||
return ""
|
||||
var payload = p_token.split('.')[1];
|
||||
var pad_length = ceil(payload.length() / 4.0) * 4;
|
||||
# Pad base64
|
||||
for i in range(0, pad_length - payload.length()):
|
||||
payload += "="
|
||||
payload = payload.replace("-", "+").replace("_", "/")
|
||||
return Marshalls.base64_to_utf8(payload)
|
@ -0,0 +1,34 @@
|
||||
extends Reference
|
||||
class_name NakamaStorageObjectId
|
||||
|
||||
# The collection which stores the object.
|
||||
var collection : String
|
||||
|
||||
# The key of the object within the collection.
|
||||
var key : String
|
||||
|
||||
# The user owner of the object.
|
||||
var user_id : String
|
||||
|
||||
# The version hash of the object.
|
||||
var version : String
|
||||
|
||||
func _init(p_collection, p_key, p_user_id = "", p_version = ""):
|
||||
collection = p_collection
|
||||
key = p_key
|
||||
user_id = p_user_id
|
||||
version = p_version
|
||||
|
||||
func as_delete():
|
||||
return NakamaAPI.ApiDeleteStorageObjectId.create(NakamaAPI, {
|
||||
"collection": collection,
|
||||
"key": key,
|
||||
"version": version
|
||||
})
|
||||
|
||||
func as_read():
|
||||
return NakamaAPI.ApiReadStorageObjectId.create(NakamaAPI, {
|
||||
"collection": collection,
|
||||
"key": key,
|
||||
"user_id": user_id
|
||||
})
|
@ -0,0 +1,28 @@
|
||||
extends Reference
|
||||
class_name NakamaWriteStorageObject
|
||||
|
||||
var collection : String
|
||||
var key : String
|
||||
var permission_read : int = 0
|
||||
var permission_write : int = 0
|
||||
var value : String
|
||||
var version : String
|
||||
|
||||
func _init(p_collection : String, p_key : String, p_permission_read : int,
|
||||
p_permission_write : int, p_value : String, p_version : String):
|
||||
collection = p_collection
|
||||
key = p_key
|
||||
permission_read = p_permission_read
|
||||
permission_write = p_permission_write
|
||||
value = p_value
|
||||
version = p_version
|
||||
|
||||
func as_write():
|
||||
return NakamaAPI.ApiWriteStorageObject.create(NakamaAPI, {
|
||||
"collection": collection,
|
||||
"key": key,
|
||||
"permission_read": permission_read,
|
||||
"permission_write": permission_write,
|
||||
"value": value,
|
||||
"version": version
|
||||
})
|
795
client/addons/com.heroiclabs.nakama/client/NakamaClient.gd
Normal file
795
client/addons/com.heroiclabs.nakama/client/NakamaClient.gd
Normal file
@ -0,0 +1,795 @@
|
||||
extends Reference
|
||||
|
||||
# A client for the API in Nakama server.
|
||||
class_name NakamaClient
|
||||
|
||||
const ChannelType = NakamaRTMessage.ChannelJoin.ChannelType
|
||||
|
||||
func _no_set(_p):
|
||||
return
|
||||
|
||||
func _no_get():
|
||||
return null
|
||||
|
||||
# The host address of the server. Defaults to "127.0.0.1".
|
||||
var host : String setget _no_set
|
||||
|
||||
# The port number of the server. Defaults to 7350.
|
||||
var port : int setget _no_set
|
||||
|
||||
# The protocol scheme used to connect with the server. Must be either "http" or "https".
|
||||
var scheme : String setget _no_set
|
||||
|
||||
# The key used to authenticate with the server without a session. Defaults to "defaultkey".
|
||||
var server_key : String = "defaultkey" setget _no_set
|
||||
|
||||
# Set the timeout in seconds on requests sent to the server.
|
||||
var timeout : int
|
||||
|
||||
var logger : NakamaLogger = null
|
||||
|
||||
var _api_client := NakamaAPI.ApiClient.new("", NakamaAPI, null) setget _no_set, _no_get
|
||||
|
||||
func _init(p_adapter : NakamaHTTPAdapter,
|
||||
p_server_key : String,
|
||||
p_scheme : String,
|
||||
p_host : String,
|
||||
p_port : int,
|
||||
p_timeout : int):
|
||||
|
||||
server_key = p_server_key
|
||||
scheme = p_scheme
|
||||
host = p_host
|
||||
port = p_port
|
||||
timeout = p_timeout
|
||||
logger = p_adapter.logger
|
||||
_api_client = NakamaAPI.ApiClient.new(scheme + "://" + host + ":" + str(port), p_adapter, NakamaAPI, p_timeout)
|
||||
|
||||
# Restore a session from the auth token.
|
||||
# A `null` or empty authentication token will return `null`.
|
||||
# @param authToken - The authentication token to restore as a session.
|
||||
# Returns a session.
|
||||
static func restore_session(auth_token : String):
|
||||
return NakamaSession.new(auth_token, false)
|
||||
|
||||
func _to_string():
|
||||
return "Client(Host='%s', Port=%s, Scheme='%s', ServerKey='%s', Timeout=%s)" % [
|
||||
host, port, scheme, server_key, timeout
|
||||
]
|
||||
|
||||
func _parse_auth(p_session) -> NakamaSession:
|
||||
if p_session.is_exception():
|
||||
return NakamaSession.new(null, false, p_session.get_exception())
|
||||
return NakamaSession.new(p_session.token, p_session.created)
|
||||
|
||||
# Add one or more friends by id or username.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_ids - The ids of the users to add or invite as friends.
|
||||
# @param p_usernames - The usernames of the users to add as friends.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func add_friends_async(p_session : NakamaSession, p_ids = null, p_usernames = null) -> NakamaAsyncResult:
|
||||
return _api_client.add_friends_async(p_session.token, p_ids, p_usernames)
|
||||
|
||||
# Add one or more users to the group.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_group_id - The id of the group to add users into.
|
||||
# @param p_ids - The ids of the users to add or invite to the group.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func add_group_users_async(p_session : NakamaSession, p_group_id : String, p_ids : PoolStringArray) -> NakamaAsyncResult:
|
||||
return _api_client.add_group_users_async(p_session.token, p_group_id, p_ids);
|
||||
|
||||
# Authenticate a user with a custom id.
|
||||
# @param p_id - A custom identifier usually obtained from an external authentication service.
|
||||
# @param p_username - A username used to create the user. May be `null`.
|
||||
# @param p_create - If the user should be created when authenticated.
|
||||
# @param p_vars - Extra information that will be bundled in the session token.
|
||||
# Returns a task which resolves to a session object.
|
||||
func authenticate_custom_async(p_id : String, p_username = null, p_create : bool = true, p_vars = null) -> NakamaSession:
|
||||
return _parse_auth(yield(_api_client.authenticate_custom_async(server_key, "",
|
||||
NakamaAPI.ApiAccountCustom.create(NakamaAPI, {
|
||||
"id": p_id,
|
||||
"vars": p_vars
|
||||
}), p_create, p_username), "completed"))
|
||||
|
||||
# Authenticate a user with a device id.
|
||||
# @param p_id - A device identifier usually obtained from a platform API.
|
||||
# @param p_username - A username used to create the user. May be `null`.
|
||||
# @param p_create - If the user should be created when authenticated.
|
||||
# @param p_vars - Extra information that will be bundled in the session token.
|
||||
# Returns a task which resolves to a session object.
|
||||
func authenticate_device_async(p_id : String, p_username = null, p_create : bool = true, p_vars = null) -> NakamaSession:
|
||||
return _parse_auth(yield(_api_client.authenticate_device_async(server_key, "",
|
||||
NakamaAPI.ApiAccountDevice.create(NakamaAPI, {
|
||||
"id": p_id,
|
||||
"vars": p_vars
|
||||
}), p_create, p_username), "completed"))
|
||||
|
||||
# Authenticate a user with an email and password.
|
||||
# @param p_email - The email address of the user.
|
||||
# @param p_password - The password for the user.
|
||||
# @param p_username - A username used to create the user. May be `null`.
|
||||
# @param p_create - If the user should be created when authenticated.
|
||||
# @param p_vars - Extra information that will be bundled in the session token.
|
||||
# Returns a task which resolves to a session object.
|
||||
func authenticate_email_async(p_email : String, p_password : String, p_username = null, p_create : bool = true, p_vars = null) -> NakamaSession:
|
||||
return _parse_auth(yield(_api_client.authenticate_email_async(server_key, "",
|
||||
NakamaAPI.ApiAccountEmail.create(NakamaAPI, {
|
||||
"email": p_email,
|
||||
"password": p_password,
|
||||
"vars": p_vars
|
||||
}), p_create, p_username), "completed"))
|
||||
|
||||
# Authenticate a user with a Facebook auth token.
|
||||
# @param p_token - An OAuth access token from the Facebook SDK.
|
||||
# @param p_username - A username used to create the user. May be `null`.
|
||||
# @param p_create - If the user should be created when authenticated.
|
||||
# @param p_import - If the Facebook friends should be imported.
|
||||
# @param p_vars - Extra information that will be bundled in the session token.
|
||||
# Returns a task which resolves to a session object.
|
||||
func authenticate_facebook_async(p_token : String, p_username = null, p_create : bool = true, p_import : bool = true, p_vars = null) -> NakamaSession:
|
||||
return _parse_auth(yield(_api_client.authenticate_facebook_async(server_key, "",
|
||||
NakamaAPI.ApiAccountFacebook.create(NakamaAPI, {
|
||||
"token": p_token,
|
||||
"vars": p_vars
|
||||
}), p_create, p_username, p_import), "completed"))
|
||||
|
||||
# Authenticate a user with a Facebook Instant Game token against the server.
|
||||
# @param p_signed_player_info - Facebook Instant Game signed info from Facebook SDK.
|
||||
# @param p_username - A username used to create the user. May be `null`.
|
||||
# @param p_create - If the user should be created when authenticated.
|
||||
# @param p_import - If the Facebook friends should be imported.
|
||||
# @param p_vars - Extra information that will be bundled in the session token.
|
||||
# Returns a task which resolves to a session object.
|
||||
func authenticate_facebook_instant_game_async(p_signed_player_info : String, p_username = null, p_create : bool = true, p_vars = null) -> NakamaSession:
|
||||
return _parse_auth(yield(_api_client.authenticate_facebook_instant_game_async(server_key, "",
|
||||
NakamaAPI.ApiAccountFacebookInstantGame.create(NakamaAPI, {
|
||||
"signed_player_info": p_signed_player_info,
|
||||
"vars": p_vars
|
||||
}), p_create, p_username), "completed"))
|
||||
|
||||
# Authenticate a user with Apple Game Center.
|
||||
# @param p_bundle_id - The bundle id of the Game Center application.
|
||||
# @param p_player_id - The player id of the user in Game Center.
|
||||
# @param p_public_key_url - The URL for the public encryption key.
|
||||
# @param p_salt - A random `NSString` used to compute the hash and keep it randomized.
|
||||
# @param p_signature - The verification signature data generated.
|
||||
# @param p_timestamp_seconds - The date and time that the signature was created.
|
||||
# @param p_username - A username used to create the user. May be `null`.
|
||||
# @param p_create - If the user should be created when authenticated.
|
||||
# @param p_vars - Extra information that will be bundled in the session token.
|
||||
# Returns a task which resolves to a session object.
|
||||
func authenticate_game_center_async(p_bundle_id : String, p_player_id : String, p_public_key_url : String,
|
||||
p_salt : String, p_signature : String, p_timestamp_seconds : String, p_username = null, p_create : bool = true, p_vars = null) -> NakamaSession:
|
||||
return _parse_auth(yield(_api_client.authenticate_game_center_async(server_key, "",
|
||||
NakamaAPI.ApiAccountGameCenter.create(NakamaAPI, {
|
||||
"bundle_id": p_bundle_id,
|
||||
"player_id": p_player_id,
|
||||
"public_key_url": p_public_key_url,
|
||||
"salt": p_salt,
|
||||
"signature": p_signature,
|
||||
"timestamp_seconds": p_timestamp_seconds,
|
||||
"vars": p_vars
|
||||
}), p_create, p_username), "completed"))
|
||||
|
||||
# Authenticate a user with a Google auth token.
|
||||
# @param p_token - An OAuth access token from the Google SDK.
|
||||
# @param p_username - A username used to create the user. May be `null`.
|
||||
# @param p_create - If the user should be created when authenticated.
|
||||
# @param p_vars - Extra information that will be bundled in the session token.
|
||||
# Returns a task which resolves to a session object.
|
||||
func authenticate_google_async(p_token : String, p_username = null, p_create : bool = true, p_vars = null) -> NakamaSession:
|
||||
return _parse_auth(yield(_api_client.authenticate_google_async(server_key, "",
|
||||
NakamaAPI.ApiAccountGoogle.create(NakamaAPI, {
|
||||
"token": p_token,
|
||||
"vars": p_vars
|
||||
}), p_create, p_username), "completed"))
|
||||
|
||||
# Authenticate a user with a Steam auth token.
|
||||
# @param p_token - An authentication token from the Steam network.
|
||||
# @param p_username - A username used to create the user. May be `null`.
|
||||
# @param p_create - If the user should be created when authenticated.
|
||||
# @param p_vars - Extra information that will be bundled in the session token.
|
||||
# Returns a task which resolves to a session object.
|
||||
func authenticate_steam_async(p_token : String, p_username = null, p_create : bool = true, p_vars = null) -> NakamaSession:
|
||||
return _parse_auth(yield(_api_client.authenticate_steam_async(server_key, "",
|
||||
NakamaAPI.ApiAccountSteam.create(NakamaAPI, {
|
||||
"token": p_token,
|
||||
"vars": p_vars
|
||||
}), p_create, p_username), "completed"))
|
||||
|
||||
# Block one or more friends by id or username.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_ids - The ids of the users to block.
|
||||
# @param p_usernames - The usernames of the users to block.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func block_friends_async(p_session : NakamaSession, p_ids : PoolStringArray, p_usernames = null) -> NakamaAsyncResult:
|
||||
return _api_client.block_friends_async(p_session.token, p_ids, p_usernames);
|
||||
|
||||
# Create a group.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_name - The name for the group.
|
||||
# @param p_description - A description for the group.
|
||||
# @param p_avatar_url - An avatar url for the group.
|
||||
# @param p_lang_tag - A language tag in BCP-47 format for the group.
|
||||
# @param p_open - If the group should have open membership.
|
||||
# @param p_max_count - The maximum number of members allowed.
|
||||
# Returns a task which resolves to a new group object.
|
||||
func create_group_async(p_session : NakamaSession, p_name : String, p_description : String = "",
|
||||
p_avatar_url = null, p_lang_tag = null, p_open : bool = true, p_max_count : int = 100): # -> NakamaAPI.ApiGroup:
|
||||
return _api_client.create_group_async(p_session.token,
|
||||
NakamaAPI.ApiCreateGroupRequest.create(NakamaAPI, {
|
||||
"avatar_url": p_avatar_url,
|
||||
"description": p_description,
|
||||
"lang_tag": p_lang_tag,
|
||||
"max_count": p_max_count,
|
||||
"name": p_name,
|
||||
"open": p_open
|
||||
}))
|
||||
|
||||
# Delete one more or users by id or username from friends.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_ids - The user ids to remove as friends.
|
||||
# @param p_usernames - The usernames to remove as friends.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func delete_friends_async(p_session : NakamaSession, p_ids : PoolStringArray, p_usernames = null) -> NakamaAsyncResult:
|
||||
return _api_client.delete_friends_async(p_session.token, p_ids, p_usernames)
|
||||
|
||||
# Delete a group by id.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_group_id - The group id to to remove.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func delete_group_async(p_session : NakamaSession, p_group_id : String) -> NakamaAsyncResult:
|
||||
return _api_client.delete_group_async(p_session.token, p_group_id)
|
||||
|
||||
# Delete a leaderboard record.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_leaderboard_id - The id of the leaderboard with the record to be deleted.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func delete_leaderboard_record_async(p_session : NakamaSession, p_leaderboard_id : String) -> NakamaAsyncResult:
|
||||
return _api_client.delete_leaderboard_record_async(p_session.token, p_leaderboard_id)
|
||||
|
||||
# Delete one or more notifications by id.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_ids - The notification ids to remove.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func delete_notifications_async(p_session : NakamaSession, p_ids : PoolStringArray) -> NakamaAsyncResult:
|
||||
return _api_client.delete_notifications_async(p_session.token, p_ids)
|
||||
|
||||
# Delete one or more storage objects.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_ids - The ids of the objects to delete.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func delete_storage_objects_async(p_session : NakamaSession, p_ids : Array) -> NakamaAsyncResult:
|
||||
var ids : Array = []
|
||||
for id in p_ids:
|
||||
if not id is NakamaStorageObjectId:
|
||||
continue # TODO Exceptions
|
||||
var obj_id : NakamaStorageObjectId = id
|
||||
ids.append(obj_id.as_delete().serialize())
|
||||
return _api_client.delete_storage_objects_async(p_session.token,
|
||||
NakamaAPI.ApiDeleteStorageObjectsRequest.create(NakamaAPI, {
|
||||
"object_ids": ids
|
||||
}))
|
||||
|
||||
# Fetch the user account owned by the session.
|
||||
# @param p_session - The session of the user.
|
||||
# Returns a task which resolves to the account object.
|
||||
func get_account_async(p_session : NakamaSession): # -> NakamaAPI.ApiAccount:
|
||||
return _api_client.get_account_async(p_session.token)
|
||||
|
||||
# Fetch one or more users by id, usernames, and Facebook ids.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_ids - The IDs of the users to retrieve.
|
||||
# @param p_usernames - The usernames of the users to retrieve.
|
||||
# @param p_facebook_ids - The facebook IDs of the users to retrieve.
|
||||
# Returns a task which resolves to a collection of user objects.
|
||||
func get_users_async(p_session : NakamaSession, p_ids : PoolStringArray, p_usernames = null, p_facebook_ids = null): # -> NakamaAPI.ApiUsers:
|
||||
return _api_client.get_users_async(p_session.token, p_ids, p_usernames, p_facebook_ids)
|
||||
|
||||
# Import Facebook friends and add them to the user's account.
|
||||
# The server will import friends when the user authenticates with Facebook. This function can be used to be
|
||||
# explicit with the import operation.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_token - An OAuth access token from the Facebook SDK.
|
||||
# @param p_reset - If the Facebook friend import for the user should be reset.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func import_facebook_friends_async(p_session : NakamaSession, p_token : String, p_reset = null) -> NakamaAsyncResult:
|
||||
return _api_client.import_facebook_friends_async(p_session.token,
|
||||
NakamaAPI.ApiAccountFacebook.create(NakamaAPI, {
|
||||
"token": p_token
|
||||
}), p_reset)
|
||||
|
||||
# Join a group if it has open membership or request to join it.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_group_id - The ID of the group to join.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func join_group_async(p_session : NakamaSession, p_group_id : String) -> NakamaAsyncResult:
|
||||
return _api_client.join_group_async(p_session.token, p_group_id)
|
||||
|
||||
# Join a tournament by ID.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_tournament_id - The ID of the tournament to join.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func join_tournament_async(p_session : NakamaSession, p_tournament_id : String) -> NakamaAsyncResult:
|
||||
return _api_client.join_tournament_async(p_session.token, p_tournament_id)
|
||||
|
||||
# Kick one or more users from the group.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_group_id - The ID of the group.
|
||||
# @param p_ids - The IDs of the users to kick.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func kick_group_users_async(p_session : NakamaSession, p_group_id : String, p_ids : PoolStringArray) -> NakamaAsyncResult:
|
||||
return _api_client.kick_group_users_async(p_session.token, p_group_id, p_ids)
|
||||
|
||||
# Leave a group by ID.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_group_id - The ID of the group to leave.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func leave_group_async(p_session : NakamaSession, p_group_id : String) -> NakamaAsyncResult:
|
||||
return _api_client.leave_group_async(p_session.token, p_group_id)
|
||||
|
||||
# Link a custom ID to the user account owned by the session.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_id - A custom identifier usually obtained from an external authentication service.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func link_custom_async(p_session : NakamaSession, p_id : String) -> NakamaAsyncResult:
|
||||
return _api_client.link_custom_async(p_session.token, NakamaAPI.ApiAccountCustom.create(NakamaAPI, {
|
||||
"id": p_id
|
||||
}))
|
||||
|
||||
# Link a device ID to the user account owned by the session.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_id - A device identifier usually obtained from a platform API.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func link_device_async(p_session : NakamaSession, p_id : String) -> NakamaAsyncResult:
|
||||
return _api_client.link_device_async(p_session.token, NakamaAPI.ApiAccountDevice.create(NakamaAPI, {
|
||||
"id": p_id
|
||||
}))
|
||||
|
||||
# Link an email with password to the user account owned by the session.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_email - The email address of the user.
|
||||
# @param p_password - The password for the user.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func link_email_async(p_session : NakamaSession, p_email : String, p_password : String) -> NakamaAsyncResult:
|
||||
return _api_client.link_email_async(p_session.token, NakamaAPI.ApiAccountEmail.create(NakamaAPI, {
|
||||
"email": p_email,
|
||||
"password": p_password
|
||||
}))
|
||||
|
||||
# Link a Facebook profile to a user account.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_token - An OAuth access token from the Facebook SDK.
|
||||
# @param p_import - If the Facebook friends should be imported.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func link_facebook_async(p_session : NakamaSession, p_token : String) -> NakamaAsyncResult:
|
||||
return _api_client.link_facebook_async(p_session.token, NakamaAPI.ApiAccountFacebook.create(NakamaAPI, {
|
||||
"token": p_token
|
||||
}))
|
||||
|
||||
# Add Facebook Instant Game to the social profiles on the current user's account.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_token - An OAuth access token from the Facebook SDK.
|
||||
# @param p_import - If the Facebook friends should be imported.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func link_facebook_instant_game_async(p_session : NakamaSession, p_signed_player_info : String) -> NakamaAsyncResult:
|
||||
return _api_client.link_facebook_instant_game_async(
|
||||
p_session.token,
|
||||
NakamaAPI.ApiAccountFacebookInstantGame.create(
|
||||
NakamaAPI, {
|
||||
"signed_player_info": p_signed_player_info
|
||||
})
|
||||
)
|
||||
|
||||
# Link a Game Center profile to a user account.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_bundle_id - The bundle ID of the Game Center application.
|
||||
# @param p_player_id - The player ID of the user in Game Center.
|
||||
# @param p_public_key_url - The URL for the public encryption key.
|
||||
# @param p_salt - A random `NSString` used to compute the hash and keep it randomized.
|
||||
# @param p_signature - The verification signature data generated.
|
||||
# @param p_timestamp_seconds - The date and time that the signature was created.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func link_game_center_async(p_session : NakamaSession,
|
||||
p_bundle_id : String, p_player_id : String, p_public_key_url : String, p_salt : String, p_signature : String, p_timestamp_seconds) -> NakamaAsyncResult:
|
||||
return _api_client.link_game_center_async(p_session.token,
|
||||
NakamaAPI.ApiAccountGameCenter.create(NakamaAPI, {
|
||||
"bundle_id": p_bundle_id,
|
||||
"player_id": p_player_id,
|
||||
"public_key_url": p_public_key_url,
|
||||
"salt": p_salt,
|
||||
"signature": p_signature,
|
||||
"timestamp_seconds": p_timestamp_seconds,
|
||||
}))
|
||||
|
||||
# Link a Google profile to a user account.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_token - An OAuth access token from the Google SDK.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func link_google_async(p_session : NakamaSession, p_token : String) -> NakamaAsyncResult:
|
||||
return _api_client.link_google_async(p_session.token, NakamaAPI.ApiAccountGoogle.create(NakamaAPI, {
|
||||
"token": p_token
|
||||
}))
|
||||
|
||||
# Link a Steam profile to a user account.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_token - An authentication token from the Steam network.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func link_steam_async(p_session : NakamaSession, p_token : String) -> NakamaAsyncResult:
|
||||
return _api_client.link_steam_async(p_session.token, NakamaAPI.ApiAccountSteam.create(NakamaAPI, {
|
||||
"token": p_token
|
||||
}))
|
||||
|
||||
# List messages from a chat channel.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_channel_id - The id of the chat channel.
|
||||
# @param p_limit - The number of chat messages to list.
|
||||
# @param forward - Fetch messages forward from the current cursor (or the start).
|
||||
# @param p_cursor - A cursor for the current position in the messages history to list.
|
||||
# Returns a task which resolves to the channel message list object.
|
||||
func list_channel_messages_async(p_session : NakamaSession, p_channel_id : String, limit : int = 1,
|
||||
forward : bool = true, cursor = null): # -> NakamaAPI.ApiChannelMessageList:
|
||||
return _api_client.list_channel_messages_async(p_session.token, p_channel_id, limit, forward, cursor)
|
||||
|
||||
# List of friends of the current user.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_state - Filter by friendship state.
|
||||
# @param p_limit - The number of friends to list.
|
||||
# @param p_cursor - A cursor for the current position in the friends list.
|
||||
# Returns a task which resolves to the friend objects.
|
||||
func list_friends_async(p_session : NakamaSession, p_state = null, p_limit = null, p_cursor = null): # -> NakamaAPI.ApiFriendList:
|
||||
return _api_client.list_friends_async(p_session.token, p_limit, p_state, p_cursor)
|
||||
|
||||
# List all users part of the group.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_group_id - The ID of the group.
|
||||
# @param p_state - Filter by group membership state.
|
||||
# @param p_limit - The number of groups to list.
|
||||
# @param p_cursor - A cursor for the current position in the group listing.
|
||||
# Returns a task which resolves to the group user objects.
|
||||
func list_group_users_async(p_session : NakamaSession, p_group_id : String, p_state = null, p_limit = null, p_cursor = null): # -> NakamaAPI.ApiGroupUserList:
|
||||
return _api_client.list_group_users_async(p_session.token, p_group_id, p_limit, p_state, p_cursor)
|
||||
|
||||
# List groups on the server.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_name - The name filter to apply to the group list.
|
||||
# @param p_limit - The number of groups to list.
|
||||
# @param p_cursor - A cursor for the current position in the groups to list.
|
||||
# Returns a task to resolve group objects.
|
||||
func list_groups_async(p_session : NakamaSession, p_name = null, p_limit : int = 10, p_cursor = null): # -> NakamaAPI.ApiGroupList:
|
||||
return _api_client.list_groups_async(p_session.token, p_name, p_cursor, p_limit)
|
||||
|
||||
# List records from a leaderboard.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_leaderboard_id - The ID of the leaderboard to list.
|
||||
# @param p_owner_ids - Record owners to fetch with the list of records.
|
||||
# @param p_expiry - Expiry in seconds (since epoch) to begin fetching records from. Optional. 0 means from current time.
|
||||
# @param p_limit - The number of records to list.
|
||||
# @param p_cursor - A cursor for the current position in the leaderboard records to list.
|
||||
# Returns a task which resolves to the leaderboard record objects.
|
||||
func list_leaderboard_records_async(p_session : NakamaSession,
|
||||
p_leaderboard_id : String, p_owner_ids = null, p_expiry = null, p_limit : int = 10, p_cursor = null): # -> NakamaAPI.ApiLeaderboardRecordList:
|
||||
return _api_client.list_leaderboard_records_async(p_session.token,
|
||||
p_leaderboard_id, p_owner_ids, p_limit, p_cursor, p_expiry)
|
||||
|
||||
# List leaderboard records that belong to a user.
|
||||
# @param p_session - The session for the user.
|
||||
# @param p_leaderboard_id - The ID of the leaderboard to list.
|
||||
# @param p_owner_id - The ID of the user to list around.
|
||||
# @param p_expiry - Expiry in seconds (since epoch) to begin fetching records from. Optional. 0 means from current time.
|
||||
# @param p_limit - The limit of the listings.
|
||||
# Returns a task which resolves to the leaderboard record objects.
|
||||
func list_leaderboard_records_around_owner_async(p_session : NakamaSession,
|
||||
p_leaderboar_id : String, p_owner_id : String, p_expiry = null, p_limit : int = 10): # -> NakamaAPI.ApiLeaderboardRecordList:
|
||||
return _api_client.list_leaderboard_records_around_owner_async(p_session.token,
|
||||
p_leaderboar_id, p_owner_id, p_limit, p_expiry)
|
||||
|
||||
# Fetch a list of matches active on the server.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_min - The minimum number of match participants.
|
||||
# @param p_max - The maximum number of match participants.
|
||||
# @param p_limit - The number of matches to list.
|
||||
# @param p_authoritative - If authoritative matches should be included.
|
||||
# @param p_label - The label to filter the match list on.
|
||||
# @param p_query - A query for the matches to filter.
|
||||
# Returns a task which resolves to the match list object.
|
||||
func list_matches_async(p_session : NakamaSession, p_min : int, p_max : int, p_limit : int, p_authoritative : bool,
|
||||
p_label : String, p_query : String): # -> NakamaAPI.ApiMatchList:
|
||||
return _api_client.list_matches_async(p_session.token, p_limit, p_authoritative, p_label, p_min, p_max, p_query)
|
||||
|
||||
# List notifications for the user with an optional cursor.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_limit - The number of notifications to list.
|
||||
# @param p_cacheable_cursor - A cursor for the current position in notifications to list.
|
||||
# Returns a task to resolve notifications objects.
|
||||
func list_notifications_async(p_session : NakamaSession, p_limit : int = 10, p_cacheable_cursor = null): # -> NakamaAPI.ApiNotificationList:
|
||||
return _api_client.list_notifications_async(p_session.token, p_limit, p_cacheable_cursor)
|
||||
|
||||
# List storage objects in a collection which have public read access.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_collection - The collection to list over.
|
||||
# @param p_user_id - The id of the user that owns the objects.
|
||||
# @param p_limit - The number of objects to list.
|
||||
# @param p_cursor - A cursor to paginate over the collection.
|
||||
# Returns a task which resolves to the storage object list.
|
||||
func list_storage_objects_async(p_session : NakamaSession, p_collection : String, p_user_id : String = "", p_limit : int = 10, p_cursor = null): # -> NakamaAPI.ApiStorageObjectList:
|
||||
# List tournament records around the owner.
|
||||
return _api_client.list_storage_objects_async(p_session.token, p_collection, p_user_id, p_limit, p_cursor)
|
||||
|
||||
# List tournament records around the owner.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_tournament_id - The ID of the tournament.
|
||||
# @param p_owner_id - The ID of the owner to pivot around.
|
||||
# @param p_expiry - Expiry in seconds (since epoch) to begin fetching records from.
|
||||
# @param p_limit - The number of records to list.
|
||||
# Returns a task which resolves to the tournament record list object.
|
||||
func list_tournament_records_around_owner_async(p_session : NakamaSession,
|
||||
p_tournament_id : String, p_owner_id : String, p_limit : int = 10, p_expiry = null): # -> NakamaAPI.ApiTournamentRecordList:
|
||||
return _api_client.list_tournament_records_around_owner_async(p_session.token, p_tournament_id, p_owner_id, p_limit, p_expiry)
|
||||
|
||||
# List records from a tournament.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_tournament_id - The ID of the tournament.
|
||||
# @param p_owner_ids - The IDs of the record owners to return in the result.
|
||||
# @param p_expiry - Expiry in seconds (since epoch) to begin fetching records from.
|
||||
# @param p_limit - The number of records to list.
|
||||
# @param p_cursor - An optional cursor for the next page of tournament records.
|
||||
# Returns a task which resolves to the list of tournament records.
|
||||
func list_tournament_records_async(p_session : NakamaSession, p_tournament_id : String,
|
||||
p_owner_ids = null, p_limit : int = 10, p_cursor = null, p_expiry = null): # -> NakamaAPI.ApiTournamentRecordList:
|
||||
return _api_client.list_tournament_records_async(p_session.token, p_tournament_id, p_owner_ids, p_limit, p_cursor, p_expiry)
|
||||
|
||||
# List current or upcoming tournaments.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_category_start - The start of the category of tournaments to include.
|
||||
# @param p_category_end - The end of the category of tournaments to include.
|
||||
# @param p_start_time - The start time of the tournaments. (UNIX timestamp)
|
||||
# @param p_end_time - The end time of the tournaments. (UNIX timestamp)
|
||||
# @param p_limit - The number of tournaments to list.
|
||||
# @param p_cursor - An optional cursor for the next page of tournaments.
|
||||
# Returns a task which resolves to the list of tournament objects.
|
||||
func list_tournaments_async(p_session : NakamaSession, p_category_start : int, p_category_end : int,
|
||||
p_start_time : int, p_end_time : int, p_limit : int = 10, p_cursor = null): # -> NakamaAPI.ApiTournamentList:
|
||||
return _api_client.list_tournaments_async(p_session.token,
|
||||
p_category_start, p_category_end, p_start_time, p_end_time, p_limit, p_cursor)
|
||||
|
||||
# List of groups the current user is a member of.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_user_id - The ID of the user whose groups to list.
|
||||
# @param p_state - Filter by group membership state.
|
||||
# @param p_limit - The number of records to list.
|
||||
# @param p_cursor - A cursor for the current position in the listing.
|
||||
# Returns a task which resolves to the group list object.
|
||||
func list_user_groups_async(p_session : NakamaSession, p_user_id : String, p_state = null, p_limit = null, p_cursor = null): # -> NakamaAPI.ApiUserGroupList:
|
||||
return _api_client.list_user_groups_async(p_session.token, p_user_id, p_limit, p_state, p_cursor)
|
||||
|
||||
# List storage objects in a collection which belong to a specific user and have public read access.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_collection - The collection to list over.
|
||||
# @param p_user_id - The user ID of the user to list objects for.
|
||||
# @param p_limit - The number of objects to list.
|
||||
# @param p_cursor - A cursor to paginate over the collection.
|
||||
# Returns a task which resolves to the storage object list.
|
||||
func list_users_storage_objects_async(p_session : NakamaSession,
|
||||
p_collection : String, p_user_id : String, p_limit : int, p_cursor : String): # -> NakamaAPI.ApiStorageObjectList:
|
||||
return _api_client.list_storage_objects2_async(p_session.token, p_collection, p_user_id, p_limit, p_cursor)
|
||||
|
||||
# Promote one or more users in the group.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_group_id - The ID of the group to promote users into.
|
||||
# @param p_ids - The IDs of the users to promote.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func promote_group_users_async(p_session : NakamaSession, p_group_id : String, p_ids : PoolStringArray) -> NakamaAsyncResult:
|
||||
return _api_client.promote_group_users_async(p_session.token, p_group_id, p_ids)
|
||||
|
||||
# Read one or more objects from the storage engine.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_ids - The objects to read.
|
||||
# Returns a task which resolves to the storage batch object.
|
||||
func read_storage_objects_async(p_session : NakamaSession, p_ids : Array): # -> NakamaAPI.ApiStorageObjects:
|
||||
var ids = []
|
||||
for id in p_ids:
|
||||
if not id is NakamaStorageObjectId:
|
||||
continue # TODO Exceptions
|
||||
var obj_id : NakamaStorageObjectId = id
|
||||
ids.append(obj_id.as_read().serialize())
|
||||
return _api_client.read_storage_objects_async(p_session.token,
|
||||
NakamaAPI.ApiReadStorageObjectsRequest.create(NakamaAPI, {
|
||||
"object_ids": ids
|
||||
}))
|
||||
|
||||
# Execute a function with an input payload on the server.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_id - The ID of the function to execute on the server.
|
||||
# @param p_payload - The payload to send with the function call.
|
||||
# Returns a task which resolves to the RPC response.
|
||||
func rpc_async(p_session : NakamaSession, p_id : String, p_payload : String = ""): # -> NakamaAPI.ApiRpc:
|
||||
return _api_client.rpc_func_async(p_session.token, p_id, p_payload)
|
||||
|
||||
# Execute a function on the server without a session.
|
||||
# This function is usually used with server side code. DO NOT USE client side.
|
||||
# @param p_http_key - The secure HTTP key used to authenticate.
|
||||
# @param p_id - The id of the function to execute on the server.
|
||||
# @param p_payload - A payload to send with the function call.
|
||||
# Returns a task to resolve an RPC response.
|
||||
func rpc_async_with_key(p_http_key : String, p_id : String, p_payload = null): # -> NakamaAPI.ApiRpc:
|
||||
return _api_client.rpc_func2_async("", p_id, p_payload, p_http_key)
|
||||
|
||||
# Unlink a custom ID from the user account owned by the session.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_id - A custom identifier usually obtained from an external authentication service.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func unlink_custom_async(p_session : NakamaSession, p_id : String) -> NakamaAsyncResult:
|
||||
return _api_client.unlink_custom_async(p_session.token, NakamaAPI.ApiAccountCustom.create(NakamaAPI, {
|
||||
"id": p_id
|
||||
}))
|
||||
|
||||
# Unlink a device ID from the user account owned by the session.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_id - A device identifier usually obtained from a platform API.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func unlink_device_async(p_session : NakamaSession, p_id : String) -> NakamaAsyncResult:
|
||||
return _api_client.unlink_device_async(p_session.token, NakamaAPI.ApiAccountDevice.create(NakamaAPI, {
|
||||
"id": p_id
|
||||
}))
|
||||
|
||||
# Unlink an email with password from the user account owned by the session.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_email - The email address of the user.
|
||||
# @param p_password - The password for the user.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func unlink_email_async(p_session : NakamaSession, p_email : String, p_password : String) -> NakamaAsyncResult:
|
||||
return _api_client.unlink_email_async(p_session.token, NakamaAPI.ApiAccountEmail.create(NakamaAPI, {
|
||||
"email": p_email,
|
||||
"password": p_password
|
||||
}))
|
||||
|
||||
# Unlink a Facebook profile from the user account owned by the session.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_token - An OAuth access token from the Facebook SDK.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func unlink_facebook_async(p_session : NakamaSession, p_token : String) -> NakamaAsyncResult:
|
||||
return _api_client.unlink_facebook_async(p_session.token, NakamaAPI.ApiAccountFacebook.create(NakamaAPI, {
|
||||
"token": p_token
|
||||
}))
|
||||
|
||||
# Unlink a Facebook profile from the user account owned by the session.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_token - An OAuth access token from the Facebook SDK.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func unlink_facebook_instant_game_async(p_session : NakamaSession, p_signed_player_info : String) -> NakamaAsyncResult:
|
||||
return _api_client.unlink_facebook_instant_game_async(
|
||||
p_session.token,
|
||||
NakamaAPI.ApiAccountFacebookInstantGame.create(NakamaAPI, {
|
||||
"signed_player_info": p_signed_player_info
|
||||
})
|
||||
)
|
||||
|
||||
# Unlink a Game Center profile from the user account owned by the session.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_bundle_id - The bundle ID of the Game Center application.
|
||||
# @param p_player_id - The player ID of the user in Game Center.
|
||||
# @param p_public_key_url - The URL for the public encryption key.
|
||||
# @param p_salt - A random `NSString` used to compute the hash and keep it randomized.
|
||||
# @param p_signature - The verification signature data generated.
|
||||
# @param p_timestamp_seconds - The date and time that the signature was created.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func unlink_game_center_async(p_session : NakamaSession,
|
||||
p_bundle_id : String, p_player_id : String, p_public_key_url : String, p_salt : String, p_signature : String, p_timestamp_seconds) -> NakamaAsyncResult:
|
||||
return _api_client.unlink_game_center_async(p_session.token,
|
||||
NakamaAPI.ApiAccountGameCenter.create(NakamaAPI, {
|
||||
"bundle_id": p_bundle_id,
|
||||
"player_id": p_player_id,
|
||||
"public_key_url": p_public_key_url,
|
||||
"salt": p_salt,
|
||||
"signature": p_signature,
|
||||
"timestamp_seconds": p_timestamp_seconds,
|
||||
}))
|
||||
|
||||
# Unlink a Google profile from the user account owned by the session.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_token - An OAuth access token from the Google SDK.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func unlink_google_async(p_session : NakamaSession, p_token : String) -> NakamaAsyncResult:
|
||||
return _api_client.unlink_google_async(p_session.token, NakamaAPI.ApiAccountGoogle.create(NakamaAPI, {
|
||||
"token": p_token
|
||||
}))
|
||||
|
||||
# Unlink a Steam profile from the user account owned by the session.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_token - An authentication token from the Steam network.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func unlink_steam_async(p_session : NakamaSession, p_token : String) -> NakamaAsyncResult:
|
||||
return _api_client.unlink_steam_async(p_session.token, NakamaAPI.ApiAccountSteam.create(NakamaAPI, {
|
||||
"token": p_token
|
||||
}))
|
||||
|
||||
# Update the current user's account on the server.
|
||||
# @param p_session - The session for the user.
|
||||
# @param p_username - The new username for the user.
|
||||
# @param p_display_name - A new display name for the user.
|
||||
# @param p_avatar_url - A new avatar url for the user.
|
||||
# @param p_lang_tag - A new language tag in BCP-47 format for the user.
|
||||
# @param p_location - A new location for the user.
|
||||
# @param p_timezone - New timezone information for the user.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func update_account_async(p_session : NakamaSession, p_username = null, p_display_name = null,
|
||||
p_avatar_url = null, p_lang_tag = null, p_location = null, p_timezone = null) -> NakamaAsyncResult:
|
||||
return _api_client.update_account_async(p_session.token,
|
||||
NakamaAPI.ApiUpdateAccountRequest.create(NakamaAPI, {
|
||||
"avatar_url": p_avatar_url,
|
||||
"display_name": p_display_name,
|
||||
"lang_tag": p_lang_tag,
|
||||
"location": p_location,
|
||||
"timezone": p_timezone,
|
||||
"username": p_username
|
||||
}))
|
||||
|
||||
# Update a group.
|
||||
# The user must have the correct access permissions for the group.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_group_id - The ID of the group to update.
|
||||
# @param p_name - A new name for the group.
|
||||
# @param p_open - If the group should have open membership.
|
||||
# @param p_description - A new description for the group.
|
||||
# @param p_avatar_url - A new avatar url for the group.
|
||||
# @param p_lang_tag - A new language tag in BCP-47 format for the group.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func update_group_async(p_session : NakamaSession,
|
||||
p_group_id : String, p_name = null, p_description = null, p_avatar_url = null, p_lang_tag = null, p_open = null) -> NakamaAsyncResult:
|
||||
return _api_client.update_group_async(p_session.token, p_group_id,
|
||||
NakamaAPI.ApiUpdateGroupRequest.create(NakamaAPI, {
|
||||
"name": p_name,
|
||||
"open": p_open,
|
||||
"avatar_url": p_avatar_url,
|
||||
"description": p_description,
|
||||
"lang_tag": p_lang_tag
|
||||
}))
|
||||
|
||||
# Write a record to a leaderboard.
|
||||
# @param p_session - The session for the user.
|
||||
# @param p_leaderboard_id - The ID of the leaderboard to write.
|
||||
# @param p_score - The score for the leaderboard record.
|
||||
# @param p_subscore - The subscore for the leaderboard record.
|
||||
# @param p_metadata - The metadata for the leaderboard record.
|
||||
# Returns a task which resolves to the leaderboard record object written.
|
||||
func write_leaderboard_record_async(p_session : NakamaSession,
|
||||
p_leaderboard_id : String, p_score : int, p_subscore : int = 0, p_metadata = null): # -> NakamaAPI.ApiLeaderboardRecord:
|
||||
return _api_client.write_leaderboard_record_async(p_session.token, p_leaderboard_id,
|
||||
NakamaAPI.WriteLeaderboardRecordRequestLeaderboardRecordWrite.create(NakamaAPI, {
|
||||
"metadata": p_metadata,
|
||||
"score": str(p_score),
|
||||
"subscore": str(p_subscore)
|
||||
}))
|
||||
|
||||
# Write objects to the storage engine.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_objects - The objects to write.
|
||||
# Returns a task which resolves to the storage write acknowledgements.
|
||||
func write_storage_objects_async(p_session : NakamaSession, p_objects : Array): # -> NakamaAPI.ApiStorageObjectAcks:
|
||||
var writes : Array = []
|
||||
for obj in p_objects:
|
||||
if not obj is NakamaWriteStorageObject:
|
||||
continue # TODO Exceptions
|
||||
var write_obj : NakamaWriteStorageObject = obj
|
||||
writes.append(write_obj.as_write().serialize())
|
||||
return _api_client.write_storage_objects_async(p_session.token,
|
||||
NakamaAPI.ApiWriteStorageObjectsRequest.create(NakamaAPI, {
|
||||
"objects": writes
|
||||
}))
|
||||
|
||||
# Write a record to a tournament.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_tournament_id - The ID of the tournament to write.
|
||||
# @param p_score - The score of the tournament record.
|
||||
# @param p_subscore - The subscore for the tournament record.
|
||||
# @param p_metadata - The metadata for the tournament record.
|
||||
# Returns a task which resolves to the tournament record object written.
|
||||
func write_tournament_record_async(p_session : NakamaSession,
|
||||
p_tournament_id : String, p_score : int, p_subscore : int = 0, p_metadata = null): # -> NakamaAPI.ApiLeaderboardRecord:
|
||||
return _api_client.write_tournament_record_async(p_session.token, p_tournament_id,
|
||||
NakamaAPI.WriteTournamentRecordRequestTournamentRecordWrite.create(NakamaAPI, {
|
||||
"metadata": p_metadata,
|
||||
"score": str(p_score),
|
||||
"subscore": str(p_subscore)
|
||||
}))
|
116
client/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd
Normal file
116
client/addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd
Normal file
@ -0,0 +1,116 @@
|
||||
tool
|
||||
extends Node
|
||||
|
||||
# An adapter which implements the HTTP protocol.
|
||||
class_name NakamaHTTPAdapter
|
||||
|
||||
# The logger to use with the adapter.
|
||||
var logger : Reference = NakamaLogger.new()
|
||||
|
||||
var _pending = {}
|
||||
var id : int = 0
|
||||
|
||||
# Send a HTTP request.
|
||||
# @param method - HTTP method to use for this request.
|
||||
# @param uri - The fully qualified URI to use.
|
||||
# @param headers - Request headers to set.
|
||||
# @param body - Request content body to set.
|
||||
# @param timeoutSec - Request timeout.
|
||||
# Returns a task which resolves to the contents of the response.
|
||||
func send_async(p_method : String, p_uri : String, p_headers : Dictionary, p_body : PoolByteArray, p_timeout : int = 3):
|
||||
var req = HTTPRequest.new()
|
||||
if OS.get_name() != 'HTML5':
|
||||
req.use_threads = true # Threads not available nor needed on the web.
|
||||
|
||||
# Parse method
|
||||
var method = HTTPClient.METHOD_GET
|
||||
if p_method == "POST":
|
||||
method = HTTPClient.METHOD_POST
|
||||
elif p_method == "PUT":
|
||||
method = HTTPClient.METHOD_PUT
|
||||
elif p_method == "DELETE":
|
||||
method = HTTPClient.METHOD_DELETE
|
||||
elif p_method == "HEAD":
|
||||
method = HTTPClient.METHOD_HEAD
|
||||
var headers = PoolStringArray()
|
||||
|
||||
# Parse headers
|
||||
headers.append("Accept: application/json")
|
||||
for k in p_headers:
|
||||
headers.append("%s: %s" % [k, p_headers[k]])
|
||||
|
||||
# Handle timeout for 3.1 compatibility
|
||||
id += 1
|
||||
_pending[id] = [req, OS.get_ticks_msec() + (p_timeout * 1000)]
|
||||
|
||||
logger.debug("Sending request [ID: %d, Method: %s, Uri: %s, Headers: %s, Body: %s, Timeout: %d]" % [
|
||||
id, p_method, p_uri, p_headers, p_body.get_string_from_utf8(), p_timeout
|
||||
])
|
||||
|
||||
add_child(req)
|
||||
return _send_async(req, p_uri, headers, method, p_body, id, _pending, logger)
|
||||
|
||||
func _process(delta):
|
||||
# Handle timeout for 3.1 compatibility
|
||||
var ids = _pending.keys()
|
||||
for id in ids:
|
||||
var p = _pending[id]
|
||||
if p[0].is_queued_for_deletion():
|
||||
_pending.erase(id)
|
||||
continue
|
||||
if p[1] < OS.get_ticks_msec():
|
||||
logger.debug("Request %d timed out" % id)
|
||||
p[0].cancel_request()
|
||||
_pending.erase(id)
|
||||
p[0].emit_signal("request_completed", HTTPRequest.RESULT_REQUEST_FAILED, 0, PoolStringArray(), PoolByteArray())
|
||||
|
||||
static func _send_async(request : HTTPRequest, p_uri : String, p_headers : PoolStringArray,
|
||||
p_method : int, p_body : PoolByteArray, p_id : int, p_pending : Dictionary, logger : NakamaLogger):
|
||||
|
||||
var err = request.request(p_uri, p_headers, true, p_method, p_body.get_string_from_utf8())
|
||||
if err != OK:
|
||||
yield(request.get_tree(), "idle_frame")
|
||||
logger.debug("Request %d failed to start, error: %d" % [p_id, err])
|
||||
request.queue_free()
|
||||
return NakamaException.new("Request failed")
|
||||
|
||||
var args = yield(request, "request_completed")
|
||||
var result = args[0]
|
||||
var response_code = args[1]
|
||||
var _headers = args[2]
|
||||
var body = args[3]
|
||||
|
||||
# Will be deleted next frame
|
||||
if not request.is_queued_for_deletion():
|
||||
request.queue_free()
|
||||
p_pending.erase(p_id)
|
||||
|
||||
if result != HTTPRequest.RESULT_SUCCESS:
|
||||
logger.debug("Request %d failed with result: %d, response code: %d" % [
|
||||
p_id, result, response_code
|
||||
])
|
||||
return NakamaException.new("HTTPRequest failed!", result)
|
||||
|
||||
var json : JSONParseResult = JSON.parse(body.get_string_from_utf8())
|
||||
if json.error != OK:
|
||||
logger.debug("Unable to parse request %d response. JSON error: %d, response code: %d" % [
|
||||
p_id, json.error, response_code
|
||||
])
|
||||
return NakamaException.new("Failed to decode JSON response", response_code)
|
||||
|
||||
if response_code != HTTPClient.RESPONSE_OK:
|
||||
var error = ""
|
||||
var code = -1
|
||||
if typeof(json.result) == TYPE_DICTIONARY:
|
||||
error = json.result["error"] if "error" in json.result else str(json.result)
|
||||
code = json.result["code"] if "code" in json.result else -1
|
||||
else:
|
||||
error = str(json.result)
|
||||
if typeof(error) == TYPE_DICTIONARY:
|
||||
error = JSON.print(error)
|
||||
logger.debug("Request %d returned response code: %d, RPC code: %d, error: %s" % [
|
||||
p_id, response_code, code, error
|
||||
])
|
||||
return NakamaException.new(error, response_code, code)
|
||||
|
||||
return json.result
|
433
client/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd
Normal file
433
client/addons/com.heroiclabs.nakama/socket/NakamaSocket.gd
Normal file
@ -0,0 +1,433 @@
|
||||
extends Reference
|
||||
|
||||
# A socket to interact with Nakama server.
|
||||
class_name NakamaSocket
|
||||
|
||||
const ChannelType = NakamaRTMessage.ChannelJoin.ChannelType
|
||||
|
||||
# Emitted when a socket is closed.
|
||||
signal closed()
|
||||
|
||||
# Emitted when a socket is connected.
|
||||
signal connected()
|
||||
|
||||
# Emitted when a chat channel message is received
|
||||
signal received_channel_message(p_channel_message) # ApiChannelMessage
|
||||
|
||||
# Emitted when receiving a presence change for joins and leaves with users in a chat channel.
|
||||
signal received_channel_presence(p_channel_presence) # ChannelPresenceEvent
|
||||
|
||||
# Emitted when an error occurs on the socket.
|
||||
signal received_error(p_error)
|
||||
|
||||
# Emitted when receiving a matchmaker matched message.
|
||||
signal received_matchmaker_matched(p_matchmaker_matched) # MatchmakerMatched
|
||||
|
||||
# Emitted when receiving a message from a multiplayer match.
|
||||
signal received_match_state(p_match_state) # MatchData
|
||||
|
||||
# Emitted when receiving a presence change for joins and leaves of users in a multiplayer match.
|
||||
signal received_match_presence(p_match_presence_event) # MatchPresenceEvent
|
||||
|
||||
# Emitted when receiving a notification for the current user.
|
||||
signal received_notification(p_api_notification) # ApiNotification
|
||||
|
||||
# Emitted when receiving a presence change for when a user updated their online status.
|
||||
signal received_status_presence(p_status_presence_event) # StatusPresenceEvent
|
||||
|
||||
# Emitted when receiving a presence change for joins and leaves on a realtime stream.
|
||||
signal received_stream_presence(p_stream_presence_event) # StreamPresenceEvent
|
||||
|
||||
# Emitted when receiving a message from a realtime stream.
|
||||
signal received_stream_state(p_stream_state) # StreamState
|
||||
|
||||
var _adapter : NakamaSocketAdapter
|
||||
var _free_adapter : bool = false
|
||||
var _weak_ref : WeakRef
|
||||
var _base_uri : String
|
||||
var _responses : Dictionary
|
||||
var _last_id : int = 1
|
||||
var _conn : GDScriptFunctionState = null
|
||||
var logger : NakamaLogger = null
|
||||
|
||||
func _resume_conn(p_err : int):
|
||||
if _conn:
|
||||
if p_err: # Exception
|
||||
logger.warning("Connection error: %d" % p_err)
|
||||
_conn.resume(NakamaAsyncResult.new(NakamaException.new()))
|
||||
else:
|
||||
logger.info("Connected!")
|
||||
_conn.resume(NakamaAsyncResult.new())
|
||||
call_deferred("_survive", _conn)
|
||||
_conn = null
|
||||
|
||||
func _init(p_adapter : NakamaSocketAdapter,
|
||||
p_host : String,
|
||||
p_port : int,
|
||||
p_scheme : String,
|
||||
p_free_adapter : bool = false):
|
||||
logger = p_adapter.logger
|
||||
_adapter = p_adapter
|
||||
_weak_ref = weakref(_adapter)
|
||||
var port = ""
|
||||
if (p_scheme == "ws" and p_port != 80) or (p_scheme == "wss" and p_port != 443):
|
||||
port = ":%d" % p_port
|
||||
_base_uri = "%s://%s%s" % [p_scheme, p_host, port]
|
||||
_free_adapter = p_free_adapter
|
||||
_adapter.connect("closed", self, "_closed")
|
||||
_adapter.connect("connected", self, "_connected")
|
||||
_adapter.connect("received_error", self, "_error")
|
||||
_adapter.connect("received", self, "_received")
|
||||
|
||||
func _notification(what):
|
||||
if what == NOTIFICATION_PREDELETE:
|
||||
# Is this a bug? Why can't I call a function? self is null...
|
||||
# _clear_responses()
|
||||
# _resume_conn(ERR_FILE_EOF)
|
||||
var keys = _responses.keys()
|
||||
for k in keys:
|
||||
_responses[k].resume(NakamaException.new("Cancelled!"))
|
||||
if _conn != null:
|
||||
_conn.resume(ERR_FILE_EOF)
|
||||
call_deferred("_survive", _conn)
|
||||
_conn = null
|
||||
if _weak_ref.get_ref() == null:
|
||||
return
|
||||
_adapter.close()
|
||||
if _free_adapter:
|
||||
_adapter.queue_free()
|
||||
|
||||
func _closed(p_error = null):
|
||||
emit_signal("closed")
|
||||
_resume_conn(ERR_CANT_CONNECT)
|
||||
_clear_responses()
|
||||
|
||||
func _error(p_error):
|
||||
emit_signal("received_error", p_error)
|
||||
_resume_conn(p_error)
|
||||
_clear_responses()
|
||||
|
||||
func _connected():
|
||||
emit_signal("connected")
|
||||
_resume_conn(OK)
|
||||
|
||||
func _received(p_bytes : PoolByteArray):
|
||||
var json_str = p_bytes.get_string_from_utf8()
|
||||
var json := JSON.parse(json_str)
|
||||
if json.error != OK or typeof(json.result) != TYPE_DICTIONARY:
|
||||
logger.error("Unable to parse response: %s" % json_str)
|
||||
return
|
||||
var dict : Dictionary = json.result
|
||||
var cid = dict.get("cid")
|
||||
if cid:
|
||||
if _responses.has(cid):
|
||||
_resume_response(cid, dict)
|
||||
else:
|
||||
logger.error("Invalid call id received %s" % dict)
|
||||
else:
|
||||
if dict.has("channel_message"):
|
||||
var res = NakamaAPI.ApiChannelMessage.create(NakamaAPI, dict["channel_message"])
|
||||
emit_signal("received_channel_message", res)
|
||||
elif dict.has("channel_presence_event"):
|
||||
var res = NakamaRTAPI.ChannelPresenceEvent.create(NakamaRTAPI, dict["channel_presence_event"])
|
||||
emit_signal("received_channel_presence", res)
|
||||
elif dict.has("match_data"):
|
||||
var res = NakamaRTAPI.MatchData.create(NakamaRTAPI, dict["match_data"])
|
||||
emit_signal("received_match_state", res)
|
||||
elif dict.has("match_presence_event"):
|
||||
var res = NakamaRTAPI.MatchPresenceEvent.create(NakamaRTAPI, dict["match_presence_event"])
|
||||
emit_signal("received_match_presence", res)
|
||||
elif dict.has("matchmaker_matched"):
|
||||
var res = NakamaRTAPI.MatchmakerMatched.create(NakamaRTAPI, dict["matchmaker_matched"])
|
||||
emit_signal("received_matchmaker_matched", res)
|
||||
elif dict.has("notifications"):
|
||||
var res = NakamaAPI.ApiNotificationList.create(NakamaAPI, dict["notifications"])
|
||||
for n in res.notifications:
|
||||
emit_signal("received_notification", n)
|
||||
elif dict.has("status_presence_event"):
|
||||
var res = NakamaRTAPI.StatusPresenceEvent.create(NakamaRTAPI, dict["status_presence_event"])
|
||||
emit_signal("received_status_presence", res)
|
||||
elif dict.has("stream_presence_event"):
|
||||
var res = NakamaRTAPI.StreamPresenceEvent.create(NakamaRTAPI, dict["stream_presence_event"])
|
||||
emit_signal("received_stream_presence", res)
|
||||
elif dict.has("stream_data"):
|
||||
var res = NakamaRTAPI.StreamData.create(NakamaRTAPI, dict["stream_data"])
|
||||
emit_signal("received_stream_state", res)
|
||||
else:
|
||||
logger.warning("Unhandled response: %s" % dict)
|
||||
|
||||
func _resume_response(p_id : String, p_data):
|
||||
if _responses.has(p_id):
|
||||
logger.debug("Resuming response: %s: %s" % [p_id, p_data])
|
||||
_responses[p_id].resume(p_data)
|
||||
else:
|
||||
logger.warning("Trying to resume missing response: %s: %s" % [p_id, p_data])
|
||||
|
||||
func _cancel_response(p_id : String):
|
||||
logger.debug("Cancelling response: %s" % [p_id])
|
||||
_resume_response(p_id, NakamaException.new("Request cancelled."))
|
||||
|
||||
func _clear_responses():
|
||||
var ids = _responses.keys()
|
||||
for id in ids:
|
||||
_cancel_response(id)
|
||||
|
||||
func _survive(p_ref):
|
||||
pass
|
||||
|
||||
func _parse_result(p_responses : Dictionary, p_id : String, p_type, p_ns : GDScript, p_result_key = null):
|
||||
|
||||
# Specifically defined key, or default for objject
|
||||
var result_key = p_result_key
|
||||
if p_type != NakamaAsyncResult and result_key == null:
|
||||
result_key = p_type.get_result_key()
|
||||
|
||||
# Here we yield and wait
|
||||
var data = yield() # Manually resumed
|
||||
call_deferred("_survive", p_responses[p_id])
|
||||
p_responses.erase(p_id) # Remove this request from the list of responses
|
||||
|
||||
# We got an exception, maybe the task was cancelled?
|
||||
if data is NakamaException:
|
||||
return p_type.new(data as NakamaException)
|
||||
# Error from server
|
||||
if data.has("error"):
|
||||
var err = data["error"]
|
||||
var code = -1
|
||||
var msg = str(err)
|
||||
if typeof(err) == TYPE_DICTIONARY:
|
||||
msg = err.get("message", "")
|
||||
code = err.get("code", -1)
|
||||
logger.warning("Error response from server: %s" % err)
|
||||
return p_type.new(NakamaException.new(msg, code))
|
||||
# Simple ack response
|
||||
elif p_type == NakamaAsyncResult:
|
||||
return NakamaAsyncResult.new()
|
||||
# Missing expected result key
|
||||
elif not data.has(result_key):
|
||||
logger.warning("Missing expected result key: %s" % result_key)
|
||||
return p_type.new(NakamaException.new("Missing expected result key: %s" % result_key))
|
||||
# All good, proceed with parsing
|
||||
else:
|
||||
return p_type.create(p_ns, data.get(result_key))
|
||||
|
||||
func _send_async(p_message, p_parse_type = NakamaAsyncResult, p_ns = NakamaRTAPI, p_msg_key = null, p_result_key = null):
|
||||
logger.debug("Sending async request: %s" % p_message)
|
||||
# For messages coming from the API which does not have a key defined, so we can override it
|
||||
var msg = p_msg_key
|
||||
# For regular RT messages
|
||||
if msg == null:
|
||||
msg = p_message.get_msg_key()
|
||||
var id = str(_last_id)
|
||||
_last_id += 1
|
||||
_responses[id] = _parse_result(_responses, id, p_parse_type, p_ns, p_result_key)
|
||||
var json := JSON.print({
|
||||
"cid": id,
|
||||
msg: p_message.serialize()
|
||||
})
|
||||
var err = _adapter.send(json.to_utf8())
|
||||
if err != OK:
|
||||
call_deferred("_cancel_response", id)
|
||||
return _responses[id]
|
||||
|
||||
func _connect_function():
|
||||
return yield() # Manually resumed
|
||||
|
||||
# If the socket is connected.
|
||||
func is_connected_to_host():
|
||||
return _adapter.is_connected_to_host()
|
||||
|
||||
# If the socket is connecting.
|
||||
func is_connecting_to_host():
|
||||
return _adapter.is_connecting_to_host()
|
||||
|
||||
# Close the socket connection to the server.
|
||||
func close():
|
||||
_adapter.close()
|
||||
|
||||
# Connect to the server.
|
||||
# @param p_session - The session of the user.
|
||||
# @param p_appear_online - If the user who appear online to other users.
|
||||
# @param p_connect_timeout - The time allowed for the socket connection to be established.
|
||||
# Returns a task to represent the asynchronous operation.
|
||||
func connect_async(p_session : NakamaSession, p_appear_online : bool = false, p_connect_timeout : int = 3):
|
||||
var uri = "%s/ws?lang=en&status=%s&token=%s" % [_base_uri, str(p_appear_online).to_lower(), p_session.token]
|
||||
logger.debug("Connecting to host: %s" % uri)
|
||||
_adapter.connect_to_host(uri, p_connect_timeout)
|
||||
_conn = _connect_function()
|
||||
return _conn
|
||||
|
||||
# Join the matchmaker pool and search for opponents on the server.
|
||||
# @param p_query - The matchmaker query to search for opponents.
|
||||
# @param p_min_count - The minimum number of players to compete against in a match.
|
||||
# @param p_max_count - The maximum number of players to compete against in a match.
|
||||
# @param p_string_properties - A set of key/value properties to provide to searches.
|
||||
# @param p_numeric_properties - A set of key/value numeric properties to provide to searches.
|
||||
# Returns a task which resolves to a matchmaker ticket object.
|
||||
func add_matchmaker_async(p_query : String = "*", p_min_count : int = 2, p_max_count : int = 8,
|
||||
p_string_props : Dictionary = {}, p_numeric_props : Dictionary = {}) -> NakamaRTAPI.MatchmakerTicket:
|
||||
return _send_async(
|
||||
NakamaRTMessage.MatchmakerAdd.new(p_query, p_min_count, p_max_count, p_string_props, p_numeric_props),
|
||||
NakamaRTAPI.MatchmakerTicket
|
||||
)
|
||||
|
||||
## <summary>
|
||||
## Create a multiplayer match on the server.
|
||||
## </summary>
|
||||
## Returns a task to represent the asynchronous operation.
|
||||
func create_match_async():
|
||||
return _send_async(NakamaRTMessage.MatchCreate.new(), NakamaRTAPI.Match)
|
||||
|
||||
# Subscribe to one or more users for their status updates.
|
||||
# @param p_user_ids - The IDs of users.
|
||||
# @param p_usernames - The usernames of the users.
|
||||
# Returns a task which resolves to the current statuses for the users.
|
||||
func follow_users_async(p_ids : PoolStringArray, p_usernames : PoolStringArray = []) -> NakamaRTAPI.Status:
|
||||
return _send_async(NakamaRTMessage.StatusFollow.new(p_ids, p_usernames), NakamaRTAPI.Status)
|
||||
|
||||
# Join a chat channel on the server.
|
||||
# @param p_target - The target channel to join.
|
||||
# @param p_type - The type of channel to join.
|
||||
# @param p_persistence - If chat messages should be stored.
|
||||
# @param p_hidden - If the current user should be hidden on the channel.
|
||||
# Returns a task which resolves to a chat channel object.
|
||||
func join_chat_async(p_target : String, p_type : int, p_persistence : bool = false, p_hidden : bool = false) -> NakamaRTAPI.Channel:
|
||||
return _send_async(
|
||||
NakamaRTMessage.ChannelJoin.new(p_target, p_type, p_persistence, p_hidden),
|
||||
NakamaRTAPI.Channel
|
||||
)
|
||||
|
||||
# Join a multiplayer match with the matchmaker matched object.
|
||||
# @param p_matched - A matchmaker matched object.
|
||||
# Returns a task which resolves to a multiplayer match.
|
||||
func join_matched_async(p_matched):
|
||||
var msg := NakamaRTMessage.MatchJoin.new()
|
||||
if p_matched.match_id:
|
||||
msg.match_id = p_matched.match_id
|
||||
else:
|
||||
msg.token = p_matched.token
|
||||
return _send_async(msg, NakamaRTAPI.Match)
|
||||
|
||||
# Join a multiplayer match by ID.
|
||||
# @param p_match_id - The ID of the match to attempt to join.
|
||||
# @param p_metadata - An optional set of key-value metadata pairs to be passed to the match handler.
|
||||
# Returns a task which resolves to a multiplayer match.
|
||||
func join_match_async(p_match_id : String, p_metadata = null):
|
||||
var msg := NakamaRTMessage.MatchJoin.new()
|
||||
msg.match_id = p_match_id
|
||||
return _send_async(msg, NakamaRTAPI.Match)
|
||||
|
||||
# Leave a chat channel on the server.
|
||||
## @param p_channel_id - The ID of the chat channel to leave.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func leave_chat_async(p_channel_id : String) -> NakamaAsyncResult:
|
||||
return _send_async(NakamaRTMessage.ChannelLeave.new(p_channel_id))
|
||||
|
||||
# Leave a multiplayer match on the server.
|
||||
# @param p_match_id - The multiplayer match to leave.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func leave_match_async(p_match_id : String) -> NakamaAsyncResult:
|
||||
return _send_async(NakamaRTMessage.MatchLeave.new(p_match_id))
|
||||
|
||||
# Remove a chat message from a chat channel on the server.
|
||||
# @param p_channel - The chat channel with the message to remove.
|
||||
# @param p_message_id - The ID of the chat message to remove.
|
||||
# Returns a task which resolves to an acknowledgement of the removed message.
|
||||
func remove_chat_message_async(p_channel_id : String, p_message_id : String):
|
||||
return _send_async(
|
||||
NakamaRTMessage.ChannelMessageRemove.new(p_channel_id, p_message_id),
|
||||
NakamaRTAPI.ChannelMessageAck
|
||||
)
|
||||
|
||||
# Leave the matchmaker pool with the ticket.
|
||||
# @param p_ticket - The ticket returned by the matchmaker on join.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func remove_matchmaker_async(p_ticket : String) -> NakamaAsyncResult:
|
||||
return _send_async(NakamaRTMessage.MatchmakerRemove.new(p_ticket))
|
||||
|
||||
# Execute an RPC function to the server.
|
||||
# @param p_func_id - The ID of the function to execute.
|
||||
# @param p_payload - An (optional) String payload to send to the server.
|
||||
# Returns a task which resolves to the RPC function response object.
|
||||
func rpc_async(p_func_id : String, p_payload = null) -> NakamaAPI.ApiRpc:
|
||||
var payload = p_payload
|
||||
match typeof(p_payload):
|
||||
TYPE_NIL, TYPE_STRING:
|
||||
pass
|
||||
_:
|
||||
payload = JSON.print(p_payload)
|
||||
return _send_async(NakamaAPI.ApiRpc.create(NakamaAPI, {
|
||||
"id": p_func_id,
|
||||
"payload": payload
|
||||
}), NakamaAPI.ApiRpc, NakamaAPI, "rpc", "rpc")
|
||||
|
||||
# Send input to a multiplayer match on the server.
|
||||
# When no presences are supplied the new match state will be sent to all presences.
|
||||
# @param p_match_id - The ID of the match.
|
||||
# @param p_op_code - An operation code for the input.
|
||||
# @param p_data - The input data to send.
|
||||
# @param p_presences - The presences in the match who should receive the input.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func send_match_state_async(p_match_id, p_op_code : int, p_data : String, p_presences = null):
|
||||
var req = _send_async(NakamaRTMessage.MatchDataSend.new(
|
||||
p_match_id,
|
||||
p_op_code,
|
||||
Marshalls.utf8_to_base64(p_data),
|
||||
p_presences
|
||||
))
|
||||
# This do not return a response from server, you don't really need to wait for it.
|
||||
req.call_deferred("resume", {})
|
||||
call_deferred("_survive", req)
|
||||
return req
|
||||
|
||||
# Send input to a multiplayer match on the server.
|
||||
# When no presences are supplied the new match state will be sent to all presences.
|
||||
# @param p_match_id - The ID of the match.
|
||||
# @param p_op_code - An operation code for the input.
|
||||
# @param p_data - The input data to send.
|
||||
# @param p_presences - The presences in the match who should receive the input.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func send_match_state_raw_async(p_match_id, p_op_code : int, p_data : PoolByteArray, p_presences = null):
|
||||
var req = _send_async(NakamaRTMessage.MatchDataSend.new(
|
||||
p_match_id,
|
||||
p_op_code,
|
||||
Marshalls.raw_to_base64(p_data),
|
||||
p_presences
|
||||
))
|
||||
# This do not return a response from server, you don't really need to wait for it.
|
||||
req.call_deferred("resume", {})
|
||||
call_deferred("_survive", req)
|
||||
return req
|
||||
|
||||
# Unfollow one or more users from their status updates.
|
||||
# @param p_user_ids - An array of user ids to unfollow.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func unfollow_users_async(p_ids : PoolStringArray):
|
||||
return _send_async(NakamaRTMessage.StatusUnfollow.new(p_ids))
|
||||
|
||||
# Update a chat message on a chat channel in the server.
|
||||
# @param p_channel_id - The ID of the chat channel with the message to update.
|
||||
# @param p_message_id - The ID of the message to update.
|
||||
# @param p_content - The new contents of the chat message.
|
||||
# Returns a task which resolves to an acknowledgement of the updated message.
|
||||
func update_chat_message_async(p_channel_id : String, p_message_id : String, p_content : Dictionary):
|
||||
return _send_async(
|
||||
NakamaRTMessage.ChannelMessageUpdate.new(p_channel_id, p_message_id, JSON.print(p_content)),
|
||||
NakamaRTAPI.ChannelMessageAck
|
||||
)
|
||||
|
||||
# Update the status for the current user online.
|
||||
# @param p_status - The new status for the user.
|
||||
# Returns a task which represents the asynchronous operation.
|
||||
func update_status_async(p_status : String):
|
||||
return _send_async(NakamaRTMessage.StatusUpdate.new(p_status))
|
||||
|
||||
# Send a chat message to a chat channel on the server.
|
||||
# @param p_channel_id - The ID of the chat channel to send onto.
|
||||
# @param p_content - The contents of the message to send.
|
||||
# Returns a task which resolves to the acknowledgement of the chat message write.
|
||||
func write_chat_message_async(p_channel_id : String, p_content : Dictionary):
|
||||
return _send_async(
|
||||
NakamaRTMessage.ChannelMessageSend.new(p_channel_id, JSON.print(p_content)),
|
||||
NakamaRTAPI.ChannelMessageAck
|
||||
)
|
@ -0,0 +1,82 @@
|
||||
tool
|
||||
extends Node
|
||||
|
||||
# An adapter which implements a socket with a protocol supported by Nakama.
|
||||
class_name NakamaSocketAdapter
|
||||
|
||||
var _ws := WebSocketClient.new()
|
||||
var _timeout : int = 30
|
||||
var _start : int = 0
|
||||
var logger = NakamaLogger.new()
|
||||
|
||||
# A signal emitted when the socket is connected.
|
||||
signal connected()
|
||||
|
||||
# A signal emitted when the socket is disconnected.
|
||||
signal closed()
|
||||
|
||||
# A signal emitted when the socket has an error when connected.
|
||||
signal received_error(p_exception)
|
||||
|
||||
# A signal emitted when the socket receives a message.
|
||||
signal received(p_bytes) # PoolByteArray
|
||||
|
||||
# If the socket is connected.
|
||||
func is_connected_to_host():
|
||||
return _ws.get_connection_status() == WebSocketClient.CONNECTION_CONNECTED
|
||||
|
||||
# If the socket is connecting.
|
||||
func is_connecting_to_host():
|
||||
return _ws.get_connection_status() == WebSocketClient.CONNECTION_CONNECTING
|
||||
|
||||
# Close the socket with an asynchronous operation.
|
||||
func close():
|
||||
_ws.disconnect_from_host()
|
||||
|
||||
# Connect to the server with an asynchronous operation.
|
||||
# @param p_uri - The URI of the server.
|
||||
# @param p_timeout - The timeout for the connect attempt on the socket.
|
||||
func connect_to_host(p_uri : String, p_timeout : int):
|
||||
_ws.disconnect_from_host()
|
||||
_timeout = p_timeout
|
||||
_start = OS.get_unix_time()
|
||||
var err = _ws.connect_to_url(p_uri)
|
||||
if err != OK:
|
||||
logger.debug("Error connecting to host %s" % p_uri)
|
||||
call_deferred("emit_signal", "received_error", err)
|
||||
|
||||
# Send data to the server with an asynchronous operation.
|
||||
# @param p_buffer - The buffer with the message to send.
|
||||
# @param p_reliable - If the message should be sent reliably (will be ignored by some protocols).
|
||||
func send(p_buffer : PoolByteArray, p_reliable : bool = true) -> int:
|
||||
return _ws.get_peer(1).put_packet(p_buffer)
|
||||
|
||||
func _process(delta):
|
||||
if _ws.get_connection_status() == WebSocketClient.CONNECTION_CONNECTING:
|
||||
if _start + _timeout < OS.get_unix_time():
|
||||
logger.debug("Timeout when connecting to socket")
|
||||
emit_signal("received_error", ERR_TIMEOUT)
|
||||
_ws.disconnect_from_host()
|
||||
else:
|
||||
_ws.poll()
|
||||
if _ws.get_connection_status() != WebSocketClient.CONNECTION_DISCONNECTED:
|
||||
_ws.poll()
|
||||
|
||||
func _init():
|
||||
_ws.connect("data_received", self, "_received")
|
||||
_ws.connect("connection_established", self, "_connected")
|
||||
_ws.connect("connection_error", self, "_error")
|
||||
_ws.connect("connection_closed", self, "_closed")
|
||||
|
||||
func _received():
|
||||
emit_signal("received", _ws.get_peer(1).get_packet())
|
||||
|
||||
func _connected(p_protocol : String):
|
||||
_ws.get_peer(1).set_write_mode(WebSocketPeer.WRITE_MODE_TEXT)
|
||||
emit_signal("connected")
|
||||
|
||||
func _error():
|
||||
emit_signal("received_error", FAILED)
|
||||
|
||||
func _closed(p_clean : bool):
|
||||
emit_signal("closed")
|
@ -0,0 +1,29 @@
|
||||
extends Reference
|
||||
class_name NakamaAsyncResult
|
||||
|
||||
var exception : NakamaException setget _no_set, get_exception
|
||||
var _ex = null
|
||||
|
||||
func _no_set(v):
|
||||
return
|
||||
|
||||
func _init(p_ex = null):
|
||||
_ex = p_ex
|
||||
|
||||
func is_exception():
|
||||
return get_exception() != null
|
||||
|
||||
func get_exception() -> NakamaException:
|
||||
return _ex as NakamaException
|
||||
|
||||
func _to_string():
|
||||
if is_exception():
|
||||
return get_exception()._to_string()
|
||||
return "NakamaAsyncResult<>"
|
||||
|
||||
static func _safe_ret(p_obj, p_type : GDScript):
|
||||
if p_obj is p_type:
|
||||
return p_obj # Correct type
|
||||
elif p_obj is NakamaException:
|
||||
return p_type.new(p_obj) # It's an exception. Incapsulate it
|
||||
return p_type.new(NakamaException.new()) # It's something else. generate an exception
|
20
client/addons/com.heroiclabs.nakama/utils/NakamaException.gd
Normal file
20
client/addons/com.heroiclabs.nakama/utils/NakamaException.gd
Normal file
@ -0,0 +1,20 @@
|
||||
extends Reference
|
||||
|
||||
# An exception generated during a request.
|
||||
# Usually contains at least an error message.
|
||||
class_name NakamaException
|
||||
|
||||
var status_code : int = -1 setget _no_set
|
||||
var grpc_status_code : int = -1 setget _no_set
|
||||
var message : String = "" setget _no_set
|
||||
|
||||
func _no_set(_p):
|
||||
pass
|
||||
|
||||
func _init(p_message : String = "", p_status_code : int = -1, p_grpc_status_code : int = -1):
|
||||
status_code = p_status_code
|
||||
grpc_status_code = p_grpc_status_code
|
||||
message = p_message
|
||||
|
||||
func _to_string() -> String:
|
||||
return "NakamaException(StatusCode={%s}, Message='{%s}', GrpcStatusCode={%s})" % [status_code, message, grpc_status_code]
|
38
client/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd
Normal file
38
client/addons/com.heroiclabs.nakama/utils/NakamaLogger.gd
Normal file
@ -0,0 +1,38 @@
|
||||
extends Reference
|
||||
class_name NakamaLogger
|
||||
|
||||
enum LOG_LEVEL {NONE, ERROR, WARNING, INFO, VERBOSE, DEBUG}
|
||||
|
||||
var _level = LOG_LEVEL.ERROR
|
||||
var _module = "Nakama"
|
||||
|
||||
func _init(p_module : String = "Nakama", p_level : int = LOG_LEVEL.ERROR):
|
||||
_level = p_level
|
||||
_module = p_module
|
||||
|
||||
func _log(level : int, msg):
|
||||
if level <= _level:
|
||||
if level == LOG_LEVEL.ERROR:
|
||||
printerr("=== %s : ERROR === %s" % [_module, str(msg)])
|
||||
else:
|
||||
var what = "=== UNKNOWN === "
|
||||
for k in LOG_LEVEL:
|
||||
if level == LOG_LEVEL[k]:
|
||||
what = "=== %s : %s === " % [_module, k]
|
||||
break
|
||||
print(what + str(msg))
|
||||
|
||||
func error(msg):
|
||||
_log(LOG_LEVEL.ERROR, msg)
|
||||
|
||||
func warning(msg):
|
||||
_log(LOG_LEVEL.WARNING, msg)
|
||||
|
||||
func info(msg):
|
||||
_log(LOG_LEVEL.INFO, msg)
|
||||
|
||||
func verbose(msg):
|
||||
_log(LOG_LEVEL.VERBOSE, msg)
|
||||
|
||||
func debug(msg):
|
||||
_log(LOG_LEVEL.DEBUG, msg)
|
145
client/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd
Normal file
145
client/addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd
Normal file
@ -0,0 +1,145 @@
|
||||
extends Reference
|
||||
class_name NakamaSerializer
|
||||
|
||||
static func serialize(p_obj : Object) -> Dictionary:
|
||||
var out = {}
|
||||
var schema = p_obj.get("_SCHEMA")
|
||||
if schema == null:
|
||||
return {} # No schema defined
|
||||
for k in schema:
|
||||
var prop = schema[k]
|
||||
var val = p_obj.get(prop["name"])
|
||||
if val == null:
|
||||
continue
|
||||
var type = prop["type"]
|
||||
var content = prop.get("content", TYPE_NIL)
|
||||
if typeof(content) == TYPE_STRING:
|
||||
content = TYPE_OBJECT
|
||||
var val_type = typeof(val)
|
||||
match val_type:
|
||||
TYPE_OBJECT: # Simple objects
|
||||
out[k] = serialize(val)
|
||||
TYPE_ARRAY: # Array of objects
|
||||
var arr = []
|
||||
for e in val:
|
||||
if typeof(e) != TYPE_OBJECT:
|
||||
continue
|
||||
arr.append(serialize(e))
|
||||
out[k] = arr
|
||||
TYPE_INT_ARRAY, TYPE_STRING_ARRAY: # Array of ints, bools, or strings
|
||||
var arr = []
|
||||
for e in val:
|
||||
if content == TYPE_BOOL:
|
||||
e = bool(e)
|
||||
if typeof(e) != content:
|
||||
continue
|
||||
arr.append(e)
|
||||
out[k] = arr
|
||||
TYPE_DICTIONARY: # Maps
|
||||
var dict = {}
|
||||
if content == TYPE_OBJECT: # Map of objects
|
||||
for l in val:
|
||||
if val_type != TYPE_OBJECT:
|
||||
continue
|
||||
dict[l] = serialize(val)
|
||||
else: # Map of simple types
|
||||
for l in val:
|
||||
if val_type != content:
|
||||
continue
|
||||
dict[l] = val
|
||||
_:
|
||||
out[k] = val
|
||||
return out
|
||||
|
||||
static func deserialize(p_ns : GDScript, p_cls_name : String, p_dict : Dictionary) -> Object:
|
||||
var cls : GDScript = p_ns.get(p_cls_name)
|
||||
var schema = cls.get("_SCHEMA")
|
||||
if schema == null:
|
||||
return NakamaException.new() # No schema defined
|
||||
var obj = cls.new()
|
||||
for k in schema:
|
||||
var prop = schema[k]
|
||||
var pname = prop["name"]
|
||||
var type = prop["type"]
|
||||
var required = prop["required"]
|
||||
var content = prop.get("content", TYPE_NIL)
|
||||
var type_cmp = type
|
||||
if typeof(type) == TYPE_STRING: # A class
|
||||
type_cmp = TYPE_DICTIONARY
|
||||
if type_cmp == TYPE_STRING_ARRAY or type_cmp == TYPE_INT_ARRAY: # A specialized array
|
||||
type_cmp = TYPE_ARRAY
|
||||
|
||||
var content_cmp = content
|
||||
if typeof(content) == TYPE_STRING: # A dictionary or array of classes
|
||||
content_cmp = TYPE_DICTIONARY
|
||||
|
||||
var val = p_dict.get(k, null)
|
||||
|
||||
# Ints might and up being recognized as floats. Change that if needed
|
||||
if typeof(val) == TYPE_REAL and type_cmp == TYPE_INT:
|
||||
val = int(val)
|
||||
|
||||
if typeof(val) == type_cmp:
|
||||
if typeof(type) == TYPE_STRING:
|
||||
obj.set(pname, deserialize(p_ns, type, val))
|
||||
elif type_cmp == TYPE_DICTIONARY:
|
||||
var v = {}
|
||||
for l in val:
|
||||
if typeof(content) == TYPE_STRING:
|
||||
v[l] = deserialize(p_ns, content, val[l])
|
||||
elif content == TYPE_INT:
|
||||
v[l] = int(val[l])
|
||||
elif content == TYPE_BOOL:
|
||||
v[l] = bool(val[l])
|
||||
else:
|
||||
v[l] = str(val[l])
|
||||
obj.set(pname, v)
|
||||
elif type_cmp == TYPE_ARRAY:
|
||||
var v
|
||||
match content:
|
||||
TYPE_INT, TYPE_BOOL: v = PoolIntArray()
|
||||
TYPE_STRING: v = PoolStringArray()
|
||||
_: v = Array()
|
||||
for e in val:
|
||||
if typeof(content) == TYPE_STRING:
|
||||
v.append(deserialize(p_ns, content, e))
|
||||
elif content == TYPE_INT:
|
||||
v.append(int(e))
|
||||
elif content == TYPE_BOOL:
|
||||
v.append(bool(e))
|
||||
else:
|
||||
v.append(str(e))
|
||||
obj.set(pname, v)
|
||||
else:
|
||||
obj.set(pname, val)
|
||||
elif required:
|
||||
obj._ex = NakamaException.new("ERROR [%s]: Missing or invalid required prop %s = %s:\n\t%s" % [p_cls_name, prop, p_dict.get(k), p_dict])
|
||||
return obj
|
||||
return obj
|
||||
|
||||
|
||||
###
|
||||
# Compatibility with Godot 3.1 which does not expose String.http_escape
|
||||
###
|
||||
const HEX = ["0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"]
|
||||
|
||||
static func escape_http(p_str : String) -> String:
|
||||
var out : String = ""
|
||||
for o in p_str:
|
||||
if (o == '.' or o == '-' or o == '_' or o == '~' or
|
||||
(o >= 'a' and o <= 'z') or
|
||||
(o >= 'A' and o <= 'Z') or
|
||||
(o >= '0' and o <= '9')):
|
||||
out += o
|
||||
else:
|
||||
for b in o.to_utf8():
|
||||
out += "%%%s" % to_hex(b)
|
||||
return out
|
||||
|
||||
static func to_hex(p_val : int) -> String:
|
||||
var v := p_val
|
||||
var o := ""
|
||||
while v != 0:
|
||||
o = HEX[v % 16] + o
|
||||
v /= 16
|
||||
return o
|
@ -8,17 +8,105 @@
|
||||
|
||||
config_version=4
|
||||
|
||||
_global_script_classes=[ ]
|
||||
_global_script_classes=[ {
|
||||
"base": "Reference",
|
||||
"class": "NakamaAPI",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/com.heroiclabs.nakama/api/NakamaAPI.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "NakamaAsyncResult",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/com.heroiclabs.nakama/utils/NakamaAsyncResult.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "NakamaClient",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/com.heroiclabs.nakama/client/NakamaClient.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "NakamaException",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/com.heroiclabs.nakama/utils/NakamaException.gd"
|
||||
}, {
|
||||
"base": "Node",
|
||||
"class": "NakamaHTTPAdapter",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/com.heroiclabs.nakama/client/NakamaHTTPAdapter.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "NakamaLogger",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/com.heroiclabs.nakama/utils/NakamaLogger.gd"
|
||||
}, {
|
||||
"base": "NakamaAsyncResult",
|
||||
"class": "NakamaRTAPI",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/com.heroiclabs.nakama/api/NakamaRTAPI.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "NakamaRTMessage",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/com.heroiclabs.nakama/api/NakamaRTMessage.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "NakamaSerializer",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/com.heroiclabs.nakama/utils/NakamaSerializer.gd"
|
||||
}, {
|
||||
"base": "NakamaAsyncResult",
|
||||
"class": "NakamaSession",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/com.heroiclabs.nakama/api/NakamaSession.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "NakamaSocket",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/com.heroiclabs.nakama/socket/NakamaSocket.gd"
|
||||
}, {
|
||||
"base": "Node",
|
||||
"class": "NakamaSocketAdapter",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/com.heroiclabs.nakama/socket/NakamaSocketAdapter.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "NakamaStorageObjectId",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/com.heroiclabs.nakama/api/NakamaStorageObjectId.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "NakamaWriteStorageObject",
|
||||
"language": "GDScript",
|
||||
"path": "res://addons/com.heroiclabs.nakama/api/NakamaWriteStorageObject.gd"
|
||||
} ]
|
||||
_global_script_class_icons={
|
||||
|
||||
"NakamaAPI": "",
|
||||
"NakamaAsyncResult": "",
|
||||
"NakamaClient": "",
|
||||
"NakamaException": "",
|
||||
"NakamaHTTPAdapter": "",
|
||||
"NakamaLogger": "",
|
||||
"NakamaRTAPI": "",
|
||||
"NakamaRTMessage": "",
|
||||
"NakamaSerializer": "",
|
||||
"NakamaSession": "",
|
||||
"NakamaSocket": "",
|
||||
"NakamaSocketAdapter": "",
|
||||
"NakamaStorageObjectId": "",
|
||||
"NakamaWriteStorageObject": ""
|
||||
}
|
||||
|
||||
[application]
|
||||
|
||||
config/name="Family"
|
||||
run/main_scene="res://default.tscn"
|
||||
run/main_scene="res://scenes/AuthScene.tscn"
|
||||
config/icon="res://icon.png"
|
||||
|
||||
[autoload]
|
||||
|
||||
Nakama="*res://addons/com.heroiclabs.nakama/Nakama.gd"
|
||||
ServerConnection="*res://scripts/singletons/ServerConnection.gd"
|
||||
|
||||
[rendering]
|
||||
|
||||
quality/driver/driver_name="GLES2"
|
||||
|
22
client/scenes/AuthScene.gd
Normal file
22
client/scenes/AuthScene.gd
Normal file
@ -0,0 +1,22 @@
|
||||
extends Node
|
||||
|
||||
|
||||
# Declare member variables here. Examples:
|
||||
# var a = 2
|
||||
# var b = "text"
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
return
|
||||
var result : int
|
||||
result = yield(ServerConnection.authenticate_async("j@cloudsumu.com", "Learning12!"), "completed")
|
||||
if result == null:
|
||||
print("Logged In")
|
||||
else:
|
||||
print("Auth failed! Error code: %d" % result)
|
||||
result = yield(ServerConnection.signup_async("j@cloudsumu.com", "Learing12!"), "completed")
|
||||
if result == null:
|
||||
print("Registered!")
|
||||
else:
|
||||
print("Signup failed! Error code: %d" % result)
|
153
client/scenes/AuthScene.tscn
Normal file
153
client/scenes/AuthScene.tscn
Normal file
@ -0,0 +1,153 @@
|
||||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://scenes/AuthScene.gd" type="Script" id=1]
|
||||
[ext_resource path="res://scripts/menus/signup_form.gd" type="Script" id=2]
|
||||
|
||||
[node name="AuthScene" type="Control"]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="SignupDialog" type="WindowDialog" parent="."]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
margin_left = -256.0
|
||||
margin_top = -140.0
|
||||
margin_right = 256.0
|
||||
margin_bottom = 140.0
|
||||
window_title = "Registration"
|
||||
script = ExtResource( 2 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
usernamePath = NodePath("UsernameEdit")
|
||||
passwordPath = NodePath("PasswordEdit")
|
||||
confirmPasswordPath = NodePath("CPasswordEdit")
|
||||
buttonPath = NodePath("SignupButton")
|
||||
errorPath = NodePath("ErrorLabel")
|
||||
|
||||
[node name="MessageLabel" type="Label" parent="SignupDialog"]
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
margin_left = -208.0
|
||||
margin_top = 16.0
|
||||
margin_right = 208.0
|
||||
margin_bottom = 46.0
|
||||
text = "Welcome to the family!"
|
||||
align = 1
|
||||
valign = 1
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="ErrorLabel" type="Label" parent="SignupDialog"]
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
margin_left = -232.0
|
||||
margin_top = 48.0
|
||||
margin_right = 228.0
|
||||
margin_bottom = 62.0
|
||||
custom_colors/font_color = Color( 1, 0, 0, 1 )
|
||||
text = "[Errors go here]"
|
||||
align = 1
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="UsernameLabel" type="Label" parent="SignupDialog"]
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
margin_left = -232.0
|
||||
margin_top = 72.0
|
||||
margin_right = -193.0
|
||||
margin_bottom = 86.0
|
||||
text = "Email:"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="UsernameEdit" type="LineEdit" parent="SignupDialog"]
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
margin_left = -232.0
|
||||
margin_top = 88.0
|
||||
margin_right = 232.0
|
||||
margin_bottom = 112.0
|
||||
|
||||
[node name="PasswordLabel" type="Label" parent="SignupDialog"]
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
margin_left = -232.0
|
||||
margin_top = 128.0
|
||||
margin_right = -168.0
|
||||
margin_bottom = 142.0
|
||||
text = "Password:"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="PasswordEdit" type="LineEdit" parent="SignupDialog"]
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
margin_left = -232.0
|
||||
margin_top = 144.0
|
||||
margin_right = 232.0
|
||||
margin_bottom = 168.0
|
||||
secret = true
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="CPasswordLabel" type="Label" parent="SignupDialog"]
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
margin_left = -232.0
|
||||
margin_top = 176.0
|
||||
margin_right = -168.0
|
||||
margin_bottom = 190.0
|
||||
text = "Confirm Password:"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="CPasswordEdit" type="LineEdit" parent="SignupDialog"]
|
||||
anchor_left = 0.5
|
||||
anchor_right = 0.5
|
||||
margin_left = -232.0
|
||||
margin_top = 192.0
|
||||
margin_right = 232.0
|
||||
margin_bottom = 216.0
|
||||
secret = true
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="SignupButton" type="Button" parent="SignupDialog"]
|
||||
anchor_left = 0.5
|
||||
anchor_top = 1.0
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 136.0
|
||||
margin_top = -44.0
|
||||
margin_right = 228.0
|
||||
margin_bottom = -16.0
|
||||
disabled = true
|
||||
text = "Sign Up!"
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="Button" type="Button" parent="."]
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = -109.0
|
||||
margin_top = -20.0
|
||||
text = "Create Account"
|
||||
[connection signal="button_down" from="Button" to="SignupDialog" method="popup_centered"]
|
64
client/scripts/menus/signup_form.gd
Normal file
64
client/scripts/menus/signup_form.gd
Normal file
@ -0,0 +1,64 @@
|
||||
extends Popup
|
||||
|
||||
export(NodePath) var usernamePath
|
||||
export(NodePath) var passwordPath
|
||||
export(NodePath) var confirmPasswordPath
|
||||
export(NodePath) var buttonPath
|
||||
export(NodePath) var errorPath
|
||||
|
||||
var usernameEdit : LineEdit
|
||||
var passwordEdit : LineEdit
|
||||
var cPasswordEdit : LineEdit
|
||||
var errorLabel : Label
|
||||
var button : Button
|
||||
|
||||
const MIN_PASSWORD_LENGTH = 8
|
||||
|
||||
func _ready():
|
||||
# Get nodes
|
||||
usernameEdit = get_node(usernamePath)
|
||||
passwordEdit = get_node(passwordPath)
|
||||
cPasswordEdit = get_node(confirmPasswordPath)
|
||||
errorLabel = get_node(errorPath)
|
||||
button = get_node(buttonPath)
|
||||
|
||||
# Set forms to validate on value chagne
|
||||
usernameEdit.connect("text_changed", self, "validate_fields")
|
||||
passwordEdit.connect("text_changed", self, "validate_fields")
|
||||
cPasswordEdit.connect("text_changed", self, "validate_fields")
|
||||
|
||||
# Connect submission button
|
||||
button.connect("button_down", self, "signup")
|
||||
|
||||
# Clear error message
|
||||
errorLabel.text = ""
|
||||
|
||||
func signup():
|
||||
var error : NakamaException = yield(ServerConnection.signup_async(usernameEdit.text, passwordEdit.text), "completed")
|
||||
|
||||
# Check for error
|
||||
if error:
|
||||
errorLabel.text = error.message
|
||||
else:
|
||||
print("Signed up successfully!")
|
||||
# Close signup form
|
||||
hide()
|
||||
|
||||
func validate_fields(_text=""):
|
||||
var valid : bool = check_email(usernameEdit.text) and passwords_valid(passwordEdit.text, cPasswordEdit.text)
|
||||
button.disabled = !valid
|
||||
return valid
|
||||
|
||||
func passwords_valid(password, cpassword):
|
||||
return password == cpassword and len(password) >= MIN_PASSWORD_LENGTH
|
||||
|
||||
func check_email(email) -> bool:
|
||||
# Use regex to validate email
|
||||
var regex = RegEx.new()
|
||||
regex.compile("[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,6}")
|
||||
|
||||
var result = regex.search(email)
|
||||
|
||||
if result:
|
||||
return true
|
||||
return false
|
31
client/scripts/singletons/ServerConnection.gd
Normal file
31
client/scripts/singletons/ServerConnection.gd
Normal file
@ -0,0 +1,31 @@
|
||||
extends Node
|
||||
|
||||
const KEY := "defaultkey"
|
||||
const SERVER_ENDPOINT := "nakama.cloudsumu.com"
|
||||
|
||||
var _session : NakamaSession
|
||||
var _client : NakamaClient = Nakama.create_client(KEY, SERVER_ENDPOINT, 7350, "http")
|
||||
|
||||
func authenticate_async(email : String, password : String) -> NakamaException:
|
||||
var result : NakamaException = null
|
||||
|
||||
var new_session : NakamaSession = yield(_client.authenticate_email_async(email, password, null, false), "completed")
|
||||
|
||||
if not new_session.is_exception():
|
||||
_session = new_session
|
||||
else:
|
||||
result = new_session.get_exception()
|
||||
|
||||
return result
|
||||
|
||||
func signup_async(email : String, password : String) -> NakamaException:
|
||||
var result : NakamaException = null
|
||||
|
||||
var new_session : NakamaSession = yield(_client.authenticate_email_async(email, password, null, true), "completed")
|
||||
|
||||
if not new_session.is_exception():
|
||||
_session = new_session
|
||||
else:
|
||||
result = new_session.get_exception()
|
||||
|
||||
return result
|
@ -1,4 +0,0 @@
|
||||
extends "res://addons/gut/test.gd"
|
||||
|
||||
func test_example():
|
||||
assert_true(true)
|
55
client/tests/test_signup_form.gd
Normal file
55
client/tests/test_signup_form.gd
Normal file
@ -0,0 +1,55 @@
|
||||
extends "res://addons/gut/test.gd"
|
||||
|
||||
var signup_form = load("res://scripts/menus/signup_form.gd")
|
||||
|
||||
# Test Object
|
||||
var form = signup_form.new()
|
||||
|
||||
#------------
|
||||
# Email Test
|
||||
#------------
|
||||
var valid_email_list = [
|
||||
"untitled@gmail.com",
|
||||
"test@cloudsumu.com",
|
||||
"cool.game@tetraforce.io",
|
||||
"ExampleName@yahoo.com"
|
||||
]
|
||||
|
||||
var invalid_email_list = [
|
||||
"test the test",
|
||||
"test",
|
||||
"test@test",
|
||||
"gmail.com",
|
||||
"google.com",
|
||||
"@amazon.com",
|
||||
"test@_.com",
|
||||
"test@test.",
|
||||
"Hello World!"
|
||||
]
|
||||
|
||||
func test_check_email_with_valid_email():
|
||||
for email in valid_email_list:
|
||||
assert_true(form.check_email(email))
|
||||
|
||||
func test_check_email_with_invalid_email():
|
||||
for email in invalid_email_list:
|
||||
assert_false(form.check_email(email))
|
||||
|
||||
#---------------
|
||||
# Password Test
|
||||
#---------------
|
||||
var valid_passwords = [
|
||||
"Testing123!",
|
||||
"gR8$cuP8kJ8%qk*t",
|
||||
"GVa9%BZHh",
|
||||
"2Uw@2*5Qb$Gflb@c",
|
||||
"iL3DINd@hRaBlevo"
|
||||
]
|
||||
|
||||
func test_passwords_valid_do_match():
|
||||
for password in valid_passwords:
|
||||
assert_true(form.passwords_valid(password, password))
|
||||
|
||||
func test_passwords_valid_do_not_match():
|
||||
for password in valid_passwords:
|
||||
assert_false(form.passwords_valid(password, null))
|
Reference in New Issue
Block a user