mirror of
https://codeberg.org/StreamGraph/StreamGraph.git
synced 2024-11-13 19:49:55 +01:00
120 lines
2.6 KiB
GDScript3
120 lines
2.6 KiB
GDScript3
|
# (c) 2023-present Eroax
|
||
|
# (c) 2023-present Yagich
|
||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||
|
extends Node
|
||
|
class_name RPCRenderer
|
||
|
## A WebSocket API server.
|
||
|
##
|
||
|
## A renderer that exposes a subset of the core API to remote clients using WebSocket.
|
||
|
|
||
|
const SCOPES_DIR := "res://rpc_renderer/scopes/"
|
||
|
|
||
|
@export var default_port := 6907
|
||
|
|
||
|
var _ws: WebSocketServer = WebSocketServer.new()
|
||
|
|
||
|
var scopes: Array[RPCScope]
|
||
|
var request_schema: Zodot
|
||
|
|
||
|
|
||
|
func load_scopes() -> void:
|
||
|
var d := DirAccess.open(SCOPES_DIR)
|
||
|
d.list_dir_begin()
|
||
|
var current := d.get_next()
|
||
|
while current != "":
|
||
|
if !d.current_is_dir():
|
||
|
var scope = load(SCOPES_DIR.path_join(current)).new() as RPCScope
|
||
|
scopes.append(scope)
|
||
|
|
||
|
current = d.get_next()
|
||
|
|
||
|
for scope in scopes:
|
||
|
scope.event.connect(
|
||
|
func(event: RPCEvent):
|
||
|
send_frame(0, event)
|
||
|
)
|
||
|
|
||
|
scope.response.connect(
|
||
|
func(response: RPCRequestResponse):
|
||
|
send_frame(response.peer_id, response)
|
||
|
if response.event_counterpart != null:
|
||
|
send_frame(-response.peer_id, response.event_counterpart)
|
||
|
)
|
||
|
|
||
|
|
||
|
func build_schema() -> void:
|
||
|
var scope_names = scopes.map(
|
||
|
func(scope: RPCScope):
|
||
|
return scope.name
|
||
|
)
|
||
|
|
||
|
var scopes_schema: Array[Zodot]
|
||
|
|
||
|
scopes_schema.assign(scope_names.map(
|
||
|
func(scope_name: String):
|
||
|
return Z.literal(scope_name)
|
||
|
))
|
||
|
|
||
|
request_schema = Z.schema({
|
||
|
"request": Z.schema({
|
||
|
"id": Z.string(),
|
||
|
"scope": Z.union(scopes_schema),
|
||
|
"operation": RPCOperation.schema(),
|
||
|
"keep": Z.dictionary().nullable(),
|
||
|
})
|
||
|
})
|
||
|
|
||
|
|
||
|
func _ready() -> void:
|
||
|
load_scopes()
|
||
|
build_schema()
|
||
|
|
||
|
add_child(_ws)
|
||
|
_ws.client_connected.connect(_on_ws_client_connected)
|
||
|
_ws.client_disconnected.connect(_on_ws_client_disconnected)
|
||
|
_ws.message_received.connect(_on_ws_message)
|
||
|
|
||
|
listen()
|
||
|
|
||
|
|
||
|
func listen(port := default_port) -> void:
|
||
|
if _ws.listen(port) != OK:
|
||
|
pass
|
||
|
|
||
|
|
||
|
func stop() -> void:
|
||
|
_ws.stop()
|
||
|
|
||
|
|
||
|
func send_frame(peer_id: int, frame: RPCFrame) -> void:
|
||
|
_ws.send(peer_id, JSON.stringify(frame.to_dict(), "", false))
|
||
|
|
||
|
|
||
|
func _on_ws_message(peer_id: int, message: Variant) -> void:
|
||
|
if not message is String:
|
||
|
return
|
||
|
|
||
|
var json := JSON.new()
|
||
|
var err := json.parse(message)
|
||
|
if err != OK:
|
||
|
send_frame(peer_id, RPCError.new(json.get_error_message()))
|
||
|
return
|
||
|
var result = request_schema.parse(json.get_data())
|
||
|
if not result.ok():
|
||
|
send_frame(peer_id, RPCError.new(result.error))
|
||
|
return
|
||
|
|
||
|
var req := RPCRequest.from_dict(result.data, peer_id)
|
||
|
for scope in scopes:
|
||
|
if scope.can_handle_request(req):
|
||
|
scope.handle_request(req)
|
||
|
return
|
||
|
|
||
|
|
||
|
func _on_ws_client_connected(peer_id: int) -> void:
|
||
|
pass
|
||
|
|
||
|
|
||
|
func _on_ws_client_disconnected(peer_id: int) -> void:
|
||
|
pass
|