145 lines
4 KiB
GDScript
145 lines
4 KiB
GDScript
@tool
|
|
extends Node
|
|
|
|
## Advanced websocket client that automatically reconnects to the server
|
|
class_name WebsocketClient
|
|
|
|
|
|
## Called as soon the websocket got a connection
|
|
signal connection_established
|
|
|
|
## Called as soon the websocket closed the connection
|
|
signal connection_closed
|
|
|
|
## Called when a complete message got received
|
|
signal message_received(message: PackedByteArray)
|
|
|
|
## Called when the state of the websocket changed
|
|
signal connection_state_changed(state : WebSocketPeer.State)
|
|
|
|
@export var connection_url: String:
|
|
set(val):
|
|
_logDebug("Set connection to %s" % val)
|
|
connection_url = val
|
|
|
|
var connection_state : WebSocketPeer.State = WebSocketPeer.STATE_CLOSED:
|
|
set(new_state):
|
|
if new_state != connection_state:
|
|
connection_state_changed.emit(new_state)
|
|
if new_state == WebSocketPeer.STATE_OPEN: connection_established.emit()
|
|
if new_state == WebSocketPeer.STATE_CLOSED: connection_closed.emit()
|
|
connection_state = new_state
|
|
|
|
## Determines if a connection should be established or not
|
|
@export var auto_reconnect: bool:
|
|
set(val):
|
|
auto_reconnect = val
|
|
_logDebug("New auto_reconnect value: %s" % val)
|
|
|
|
## True if currently connecting to prevent 2 connectionen processes at the same time
|
|
var _is_already_connecting: bool
|
|
var is_open: bool:
|
|
get(): return _peer.get_ready_state() == WebSocketPeer.STATE_OPEN
|
|
var is_closed: bool:
|
|
get(): return _peer.get_ready_state() == WebSocketPeer.STATE_CLOSED
|
|
|
|
var _peer: WebSocketPeer = WebSocketPeer.new()
|
|
var _tries: int
|
|
|
|
|
|
func open_connection() -> void:
|
|
if not is_closed: return
|
|
auto_reconnect = true
|
|
_logInfo("Open connection")
|
|
await _establish_connection()
|
|
|
|
|
|
func wait_connection_established() -> void:
|
|
if is_open: return
|
|
await connection_established
|
|
|
|
func _establish_connection() -> void:
|
|
if _is_already_connecting || not is_closed: return
|
|
_is_already_connecting = true
|
|
var wait_time = pow(2, _tries)
|
|
_logDebug("Wait %s before connecting" % [wait_time])
|
|
await get_tree().create_timer(wait_time, true, false, true).timeout
|
|
_logInfo("Connecting to %s" % connection_url)
|
|
var err = _peer.connect_to_url(connection_url)
|
|
if err != OK:
|
|
logError("Couldn't connect cause of %s" % [error_string(err)])
|
|
_tries += 1
|
|
_is_already_connecting = false
|
|
|
|
|
|
func _enter_tree() -> void:
|
|
if auto_reconnect: open_connection()
|
|
|
|
|
|
func _exit_tree() -> void:
|
|
if not is_open: return
|
|
_peer.close(1000, "resource got freed")
|
|
|
|
|
|
func _process(delta: float) -> void:
|
|
_poll()
|
|
|
|
|
|
func _poll() -> void:
|
|
if connection_url == "": return
|
|
|
|
var state := _peer.get_ready_state()
|
|
if state == WebSocketPeer.STATE_CLOSED and auto_reconnect:
|
|
_establish_connection()
|
|
_peer.poll()
|
|
_handle_state_changes(state)
|
|
connection_state = state
|
|
if state == WebSocketPeer.STATE_OPEN:
|
|
_read_data()
|
|
|
|
|
|
func _handle_state_changes(state: WebSocketPeer.State) -> void:
|
|
if connection_state != WebSocketPeer.STATE_OPEN && state == WebSocketPeer.STATE_OPEN:
|
|
_logInfo("connected")
|
|
_tries = 0
|
|
|
|
if connection_state != WebSocketPeer.STATE_CLOSED && state == WebSocketPeer.STATE_CLOSED:
|
|
_logInfo("connection was closed [%s]: %s" % [_peer.get_close_code(), _peer.get_close_reason()])
|
|
|
|
|
|
func _read_data() -> void:
|
|
while (_peer.get_available_packet_count()):
|
|
message_received.emit(_peer.get_packet())
|
|
|
|
|
|
func send_text(message: String) -> Error:
|
|
return _peer.send_text(message)
|
|
|
|
|
|
func close(status: int = 1000, message: String = "Normal Closure") -> void:
|
|
_logDebug("Websocket activly closed")
|
|
auto_reconnect = false
|
|
_peer.close(status, message)
|
|
|
|
|
|
# === LOGGER ===
|
|
static var logger: Dictionary = {}
|
|
static func set_logger(error: Callable, info: Callable, debug: Callable) -> void:
|
|
logger.debug = debug
|
|
logger.info = info
|
|
logger.error = error
|
|
|
|
func _logDebug(text: String) -> void:
|
|
logDebug("[%s]: %s" % [connection_url, text])
|
|
|
|
static func logDebug(text: String) -> void:
|
|
if logger.has("debug"): logger.debug.call(text)
|
|
|
|
func _logInfo(text: String) -> void:
|
|
logInfo("[%s]: %s" % [connection_url, text])
|
|
|
|
static func logInfo(text: String) -> void:
|
|
if logger.has("info"): logger.info.call(text)
|
|
|
|
static func logError(text: String) -> void:
|
|
if logger.has("error"): logger.error.call(text)
|