commit
e484ab331b
7 changed files with 130 additions and 0 deletions
@ -0,0 +1,16 @@ |
||||
# copySocket |
||||
|
||||
.. is a simple [SnekStudio] module that hosts a WebSocket server to receive hardcoded commands to affect certain things within SnekStudio from the outside. When added, it hosts a server at `ws://127.0.0.1:13981`. |
||||
|
||||
The included `send.gd` script may be used to send commands if another utility is not available, however it takes a small amount of time to start up. The following example makes the `CameraPositions` mod switch to "Camera 1". |
||||
|
||||
```sh |
||||
# --headless - Don't create a window. |
||||
# -q/--quiet - Only print errors. |
||||
# -s <script> - Run a specific script. |
||||
godot --headless -q -s path/to/send.gd -- camera 1 |
||||
``` |
||||
|
||||
If the Godot Editor is not available, the SnekStudio binary can be used in its place. |
||||
|
||||
[SnekStudio]: https://github.com/ExpiredPopsicle/SnekStudio |
||||
@ -0,0 +1,71 @@ |
||||
extends Mod_Base |
||||
|
||||
static var NUMBER_REGEX := RegEx.create_from_string("^\\d+$") |
||||
|
||||
var server: TCPServer |
||||
var peers: Dictionary[WebSocketPeer, int] |
||||
|
||||
func _ready() -> void: |
||||
server = TCPServer.new() |
||||
server.listen(13981, "127.0.0.1") |
||||
|
||||
func _exit_tree() -> void: |
||||
server.stop() |
||||
for peer in peers: |
||||
peer.close() |
||||
|
||||
server = null |
||||
peers.clear() |
||||
|
||||
func _process(_delta: float) -> void: |
||||
# Accept new connections. |
||||
while server.is_connection_available(): |
||||
var conn := server.take_connection() |
||||
if not conn: break |
||||
|
||||
var socket := WebSocketPeer.new() |
||||
# socket.supported_protocols = [] |
||||
socket.accept_stream(conn) |
||||
socket.set_no_delay(true) |
||||
|
||||
peers.set(socket, Time.get_ticks_msec()) |
||||
|
||||
var to_remove: Array[WebSocketPeer] |
||||
for peer in peers: |
||||
peer.poll() |
||||
|
||||
# Receive messages. |
||||
while peer.get_available_packet_count(): |
||||
var packet := peer.get_packet() |
||||
if not peer.was_string_packet(): continue # ignore binary packets |
||||
var message := packet.get_string_from_utf8() |
||||
_on_websocket_message(peer, message) |
||||
|
||||
# Remove the peer if it's disconnected. |
||||
if peer.get_ready_state() != WebSocketPeer.STATE_OPEN: |
||||
to_remove.append(peer) |
||||
|
||||
# Remove the peer if it's been around for over 0.5 seconds. |
||||
if Time.get_ticks_msec() - peers[peer] > 500: |
||||
to_remove.append(peer) |
||||
|
||||
# Clear out disconnected peers. |
||||
for peer in to_remove: |
||||
peers.erase(peer) |
||||
|
||||
func _on_websocket_message(peer: WebSocketPeer, message: String) -> void: |
||||
if message.begins_with("camera "): |
||||
var index := message.substr("camera ".length()) |
||||
if NUMBER_REGEX.search(index) and int(index): |
||||
var camera_mod := get_node("../CameraPositions") |
||||
if camera_mod: |
||||
camera_mod._load_camera_position("Camera " + index); |
||||
else: |
||||
print_log("mod with index 'CameraPositions' not found") |
||||
else: |
||||
print_log("invalid camera index: " + index) |
||||
else: |
||||
print_log("unknown command: " + message) |
||||
|
||||
# Process only one message, then disconnect. |
||||
peer.close() |
||||
@ -0,0 +1 @@ |
||||
uid://dme7c7r7w85pq |
||||
@ -0,0 +1,6 @@ |
||||
[gd_scene load_steps=2 format=3 uid="uid://0lluarde7l2e"] |
||||
|
||||
[ext_resource type="Script" uid="uid://dme7c7r7w85pq" path="res://Mods/copySocket/copySocket.gd" id="1_yclwn"] |
||||
|
||||
[node name="copySocket" type="Node3D"] |
||||
script = ExtResource("1_yclwn") |
||||
@ -0,0 +1 @@ |
||||
Hosts a WebSocket server to receive commands. |
||||
@ -0,0 +1,34 @@ |
||||
extends SceneTree |
||||
|
||||
func _init() -> void: |
||||
var socket = WebSocketPeer.new() |
||||
# socket.supported_protocols = [] |
||||
socket.connect_to_url("ws://127.0.0.1:13981") |
||||
socket.set_no_delay(true) |
||||
|
||||
# Wait up to 1 second for the socket to connect. |
||||
_wait_until_not(socket, WebSocketPeer.STATE_CONNECTING) |
||||
if socket.get_ready_state() != WebSocketPeer.STATE_OPEN: |
||||
push_error("could not connect"); quit(1); return |
||||
|
||||
var message := "" |
||||
for arg in OS.get_cmdline_user_args(): |
||||
if not message.is_empty(): message += " " |
||||
# TODO: Escape (and handle) quote characters? |
||||
# Surround empty string and those that contain spaces with quotes. |
||||
if arg.is_empty() or arg.contains(" "): arg = "\"" + arg + "\"" |
||||
message += arg |
||||
|
||||
socket.send_text(message) |
||||
|
||||
# Wait up to 1 second for the socket to close. |
||||
# This ensures the message makes it through. |
||||
_wait_until_not(socket, WebSocketPeer.STATE_OPEN) |
||||
quit() |
||||
|
||||
## Waits up to the specified amount for the socket to NOT be in the given state any longer. |
||||
func _wait_until_not(socket: WebSocketPeer, state: int, msec := 1000) -> void: |
||||
var start := Time.get_ticks_msec() |
||||
while socket.get_ready_state() == state: |
||||
if Time.get_ticks_msec() - start > msec: return |
||||
socket.poll() # Update websocket state. |
||||
@ -0,0 +1 @@ |
||||
uid://bdcatr7rg0gld |
||||
Loading…
Reference in new issue