+ +
+

Godot Websocket

+

Contribution by ChrisLR, 2022

+

This contrib allows you to connect a Godot Client directly to your mud, +and display regular text with color in Godot’s RichTextLabel using BBCode. +You can use Godot to provide advanced functionality with proper Evennia support.

+
+

Installation

+

You need to add the following settings in your settings.py and restart evennia.

+
PORTAL_SERVICES_PLUGIN_MODULES.append('evennia.contrib.base_systems.godotwebsocket.webclient')
+GODOT_CLIENT_WEBSOCKET_PORT = 4008
+GODOT_CLIENT_WEBSOCKET_CLIENT_INTERFACE = "127.0.0.1"
+
+
+

This will make evennia listen on the port 4008 for Godot. +You can change the port and interface as you want.

+
+
+

Usage

+

The tl;dr of it is to connect using a Godot Websocket using the port defined above. +It will let you transfer data from Evennia to Godot, allowing you +to get styled text in a RichTextLabel with bbcode enabled or to handle +the extra data given from Evennia as needed.

+

This section assumes you have basic knowledge on how to use Godot. +You can read the following url for more details on Godot Websockets +and to implement a minimal client.

+

https://docs.godotengine.org/en/stable/tutorials/networking/websocket.html

+

The rest of this document will be for Godot 3, an example is left at the bottom +of this readme for Godot 4.

+

At the top of the file you must change the url to point at your mud.

+
extends Node
+
+# The URL we will connect to
+export var websocket_url = "ws://localhost:4008"
+
+
+
+

You must also remove the protocol from the connect_to_url call made +within the _ready function.

+
func _ready(): 
+    # ...
+    # Change the following line from this
+    var err = _client.connect_to_url(websocket_url, ["lws-mirror-protocol"])
+    # To this
+    var err = _client.connect_to_url(websocket_url)
+    # ...
+
+
+

This will allow you to connect to your mud. +After that you need to properly handle the data sent by evennia. +To do this, you should replace your _on_data method. +You will need to parse the JSON received to properly act on the data. +Here is an example

+
func _on_data():
+    # The following two lines will get us the data from Evennia.
+	var data = _client.get_peer(1).get_packet().get_string_from_utf8()
+	var json_data = JSON.parse(data).result
+	# The json_data is an array
+	
+	# The first element informs us this is simple text
+	# so we add it to the RichTextlabel
+	if json_data[0] == 'text':
+		for msg in json_data[1]: 			label.append_bbcode(msg)
+	
+	# Always useful to print the data and see what we got.
+	print(data)
+
+
+

The first element is the type, it will be text if it is a message +It can be anything you would provide to the Evennia msg function. +The second element will be the data related to the type of message, in this case it is a list of text to display. +Since it is parsed BBCode, we can add that directly to a RichTextLabel by calling its append_bbcode method.

+

If you want anything better than fancy text in Godot, you will have +to leverage Evennia’s OOB to send extra data.

+

You can read more on OOB here.

+

In this example, we send coordinates whenever we message our character.

+

Evennia

+
caller.msg(coordinates=(9, 2))
+
+
+

Godot

+
func _on_data():
+    ...
+	if json_data[0] == 'text':
+		for msg in json_data[1]: 			label.append_bbcode(msg)
+	
+	# Notice the first element is the name of the kwarg we used from evennia.
+	elif json_data[0] == 'coordinates':
+		var coords_data = json_data[2]
+		player.set_pos(coords_data)
+		
+    ...
+
+
+

A good idea would be to set up Godot Signals you can trigger based on the data +you receive, so you can manage the code better.

+
+
+

Known Issues

+
    +
  • Sending SaverDicts and similar objects straight from Evennia .DB will cause issues, +cast them to dict() or list() before doing so.

  • +
  • Background colors are only supported by Godot 4.

  • +
+
+
+

Godot 3 Example

+

This is an example of a Script to use in Godot 3. +The script can be attached to the root UI node.

+
extends Node
+
+# The URL to connect to, should be your mud.
+export var websocket_url = "ws://127.0.0.1:4008"
+
+# These are references to controls in the scene
+onready var parent = get_parent()
+onready var label = parent.get_node("%ChatLog")
+onready var txtEdit = parent.get_node("%ChatInput")
+
+onready var room = get_node("/root/World/Room")
+
+# Our WebSocketClient instance
+var _client = WebSocketClient.new()
+
+var is_connected = false
+
+func _ready():
+	# Connect base signals to get notified of connection open, close, errors and messages
+	_client.connect("connection_closed", self, "_closed")
+	_client.connect("connection_error", self, "_closed")
+	_client.connect("connection_established", self, "_connected")
+	_client.connect("data_received", self, "_on_data")
+	print('Ready')
+
+	# Initiate connection to the given URL.
+	var err = _client.connect_to_url(websocket_url)
+	if err != OK:
+		print("Unable to connect")
+		set_process(false)
+
+func _closed(was_clean = false):
+	# was_clean will tell you if the disconnection was correctly notified
+	# by the remote peer before closing the socket.
+	print("Closed, clean: ", was_clean)
+	set_process(false)
+
+func _connected(proto = ""):
+	is_connected = true
+	print("Connected with protocol: ", proto)
+
+func _on_data():
+	# This is called when Godot receives data from evennia
+	var data = _client.get_peer(1).get_packet().get_string_from_utf8()
+	var json_data = JSON.parse(data).result
+	# Here we have the data from Evennia which is an array.
+	# The first element will be text if it is a message
+	# and would be the key of the OOB data you passed otherwise.
+	if json_data[0] == 'text':
+		# In this case, we simply append the data as bbcode to our label.
+		for msg in json_data[1]: 			label.append_bbcode(msg)
+	elif json_data[0] == 'coordinates':
+		# Dummy signal emitted if we wanted to handle the new coordinates
+		# elsewhere in the project.
+		self.emit_signal('updated_coordinates', json_data[1])
+
+	
+	# We only print this for easier debugging.
+	print(data)
+
+func _process(delta):
+	# Required for websocket to properly react
+	_client.poll()
+
+func _on_button_send():
+	# This is called when we press the button in the scene
+	# with a connected signal, it sends the written message to Evennia.
+	var msg = txtEdit.text
+	var msg_arr = ['text', [msg], {}]
+	var msg_str = JSON.print(msg_arr)
+	_client.get_peer(1).put_packet(msg_str.to_utf8())
+
+func _notification(what):
+	# This is a special method that allows us to notify Evennia we are closing.
+	if what == MainLoop.NOTIFICATION_WM_QUIT_REQUEST:
+		if is_connected:
+			var msg_arr = ['text', ['quit'], {}]
+			var msg_str = JSON.print(msg_arr)
+			_client.get_peer(1).put_packet(msg_str.to_utf8())
+		get_tree().quit() # default behavior
+
+
+
+
+

Godot 4 Example

+

This is an example of a Script to use in Godot 4. +Note that the version is not final so the code may break. +It requires a WebSocketClientNode as a child of the root node. +The script can be attached to the root UI node.

+
extends Control
+
+# The URL to connect to, should be your mud.
+var websocket_url = "ws://127.0.0.1:4008"
+
+# These are references to controls in the scene
+@onready
+var label: RichTextLabel = get_node("%ChatLog")
+@onready
+var txtEdit: TextEdit = get_node("%ChatInput")
+@onready
+var websocket = get_node("WebSocketClient")
+
+func _ready():
+	# We connect the various signals
+	websocket.connect('connected_to_server', self._connected)
+	websocket.connect('connection_closed', self._closed)
+	websocket.connect('message_received', self._on_data)
+	
+	# We attempt to connect and print out the error if we have one.
+	var result = websocket.connect_to_url(websocket_url)
+	if result != OK:
+		print('Could not connect:' + str(result))
+
+
+func _closed():
+	# This emits if the connection was closed by the remote host or unexpectedly
+	print('Connection closed.')
+	set_process(false)
+
+func _connected():
+	# This emits when the connection succeeds.
+	print('Connected!')
+
+func _on_data(data):
+	# This is called when Godot receives data from evennia
+	var json_data = JSON.parse_string(data)
+	# Here we have the data from Evennia which is an array.
+	# The first element will be text if it is a message
+	# and would be the key of the OOB data you passed otherwise.
+	if json_data[0] == 'text':
+		# In this case, we simply append the data as bbcode to our label.
+		for msg in json_data[1]: 			# Here we include a newline at every message.
+			label.append_text("\n" + msg)
+	elif json_data[0] == 'coordinates':
+		# Dummy signal emitted if we wanted to handle the new coordinates
+		# elsewhere in the project.
+		self.emit_signal('updated_coordinates', json_data[1])
+
+	# We only print this for easier debugging.
+	print(data)
+
+func _on_button_pressed():
+	# This is called when we press the button in the scene
+	# with a connected signal, it sends the written message to Evennia.
+	var msg = txtEdit.text
+	var msg_arr = ['text', [msg], {}]
+	var msg_str = JSON.stringify(msg_arr)
+	websocket.send(msg_str)
+
+
+
+
+

This document page is generated from evennia/contrib/base_systems/godotwebsocket/README.md. Changes to this +file will be overwritten, so edit that file rather than this one.

+
+
+ + +