From e24f4141ba2b8442c91e3367d670f0b8c571ff78 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sun, 14 Jan 2024 18:16:19 +0100 Subject: [PATCH] Update CHANGELOG. Resolve typo in sittable chair tutorial. Resolve #3408 --- CHANGELOG.md | 1 + docs/source/Coding/Changelog.md | 11 + .../source/Contribs/Contrib-Godotwebsocket.md | 304 ++++++------------ docs/source/Contribs/Contrib-RPSystem.md | 12 +- ...inner-Tutorial-Making-A-Sittable-Object.md | 15 +- ...nia.contrib.base_systems.godotwebsocket.md | 1 + ...e_systems.godotwebsocket.test_webclient.md | 10 + 7 files changed, 147 insertions(+), 207 deletions(-) create mode 100644 docs/source/api/evennia.contrib.base_systems.godotwebsocket.test_webclient.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d93ad6027..c26bcd718e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ contrib (InspectorCaracal) - [Fix][pull3405]: Fix/update of Godot client contrib to support Godot4 and latest Evennia portal changes (ChrisLR) +- Updated doc on wiki install (InspectorCaracal) - Docstring fixes (bradleymarques) - Doc tutorial fixes diff --git a/docs/source/Coding/Changelog.md b/docs/source/Coding/Changelog.md index 68754e93a1..c26bcd718e 100644 --- a/docs/source/Coding/Changelog.md +++ b/docs/source/Coding/Changelog.md @@ -2,8 +2,19 @@ ## main branch + +- [Fix][pull3398]: Fix to make e.g. `elvish"Hello"` work correctly in language rp + contrib (InspectorCaracal) +- [Fix][pull3405]: Fix/update of Godot client contrib to support Godot4 and + latest Evennia portal changes (ChrisLR) +- Updated doc on wiki install (InspectorCaracal) +- Docstring fixes (bradleymarques) - Doc tutorial fixes +[pull3398]: https://github.com/evennia/evennia/pull/3398 +[pull3405]: https://github.com/evennia/evennia/pull/3405 + + ## Evennia 3.1.0 Jan 8, 2024 diff --git a/docs/source/Contribs/Contrib-Godotwebsocket.md b/docs/source/Contribs/Contrib-Godotwebsocket.md index f007c7d5e4..ffe10e1d68 100644 --- a/docs/source/Contribs/Contrib-Godotwebsocket.md +++ b/docs/source/Contribs/Contrib-Godotwebsocket.md @@ -31,260 +31,168 @@ 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. +and to implement a minimal client or look at the full example at the bottom of this page. 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. +The rest of this document will be for Godot 4. +Note that some of the code shown here is partially taken from official Godot Documentation + +A very basic setup in godot would require + +- One RichTextLabel Node to display the Evennia Output, ensure bbcode is enabled on it. +- One Node for your websocket client code with a new Script attached. +- One TextEdit Node to enter commands +- One Button Node to press and send the commands +- Controls for the layout, in this example I have used + Panel + VBoxContainer + RichTextLabel + HBoxContainer + TextEdit + Button + +I will not go over how layout works but the documentation for them is easily accessible in the godot docs. -At the top of the file you must change the url to point at your mud. +Open up the script for your client code. + +We need to define the url leading to your mud, use the same values you have used in your Evennia Settings. +Next we write some basic code to get a connection going. +This will connect when the Scene is ready, poll and print the data when we receive it and close when the scene exits. ``` extends Node -# The URL we will connect to -export var websocket_url = "ws://localhost:4008" +# The URL we will connect to. +var websocket_url = "ws://localhost:4008" +var socket := WebSocketPeer.new() -``` - - -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) - # ... + if socket.connect_to_url(websocket_url) != OK: + print("Unable to connect.") + set_process(false) + + +func _process(_delta): + socket.poll() + match socket.get_ready_state(): + WebSocketPeer.STATE_OPEN: + while socket.get_available_packet_count(): + print(socket.get_packet().get_string_from_ascii()) + + WebSocketPeer.STATE_CLOSED: + var code = socket.get_close_code() + var reason = socket.get_close_reason() + print("WebSocket closed with code: %d, reason %s. Clean: %s" % [code, reason, code != -1]) + set_process(false) + +func _exit_tree(): + socket.close() + ``` -This will allow you to connect to your mud. +At this point, you can start your evennia server, run godot and it should print a default reply. 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. +To do this, we will add a new function to dispatch the messages properly. + 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 +func _handle_data(data): + print(data) # Print for debugging + var data_array = JSON.parse_string(data) + # The first element can be used to see if its text + if data_array[0] == 'text': + # The second element contains the messages + for msg in data_array[1]: write_to_rtb(msg) - # 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) +func write_to_rtb(msg): + output_label.append_text(msg) ``` 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. +Since it is parsed BBCode, we can add that directly to a RichTextLabel by calling its append_text 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](https://www.evennia.com/docs/latest/OOB.html#oob). -In this example, we send coordinates whenever we message our character. -Evennia -```python -caller.msg(coordinates=(9, 2)) +Now to send data, we connect the Button pressed Signal to a method, +read the label input and send it via the websocket, then clear the label. +``` +func _on_button_pressed(): + var msg = text_edit.text + var msg_arr = ['text', [msg], {}] + var msg_str = JSON.stringify(msg_arr) + socket.send_text(msg_str) + text_edit.text = "" ``` -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. +## Full Example Script ``` extends Node -# The URL to connect to, should be your mud. -export var websocket_url = "ws://127.0.0.1:4008" +# The URL we will connect to. +var websocket_url = "ws://localhost:4008" +var socket := WebSocketPeer.new() -# 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 output_label = $"../Panel/VBoxContainer/RichTextLabel" +@onready var text_edit = $"../Panel/VBoxContainer/HBoxContainer/TextEdit" -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") + if socket.connect_to_url(websocket_url) != 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 _process(_delta): + socket.poll() + match socket.get_ready_state(): + WebSocketPeer.STATE_OPEN: + while socket.get_available_packet_count(): + var data = socket.get_packet().get_string_from_ascii() + _handle_data(data) + + WebSocketPeer.STATE_CLOSED: + var code = socket.get_close_code() + var reason = socket.get_close_reason() + print("WebSocket closed with code: %d, reason %s. Clean: %s" % [code, reason, code != -1]) + set_process(false) -func _connected(proto = ""): - is_connected = true - print("Connected with protocol: ", proto) +func _handle_data(data): + print(data) # Print for debugging + var data_array = JSON.parse_string(data) + # The first element can be used to see if its text + if data_array[0] == 'text': + # The second element contains the messages + for msg in data_array[1]: write_to_rtb(msg) -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 write_to_rtb(msg): + output_label.append_text(msg) 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 = text_edit.text var msg_arr = ['text', [msg], {}] var msg_str = JSON.stringify(msg_arr) - websocket.send(msg_str) + socket.send_text(msg_str) + text_edit.text = "" + +func _exit_tree(): + socket.close() ``` - ---- This document page is generated from `evennia/contrib/base_systems/godotwebsocket/README.md`. Changes to this diff --git a/docs/source/Contribs/Contrib-RPSystem.md b/docs/source/Contribs/Contrib-RPSystem.md index fd471a1382..0be85deeb4 100644 --- a/docs/source/Contribs/Contrib-RPSystem.md +++ b/docs/source/Contribs/Contrib-RPSystem.md @@ -82,7 +82,9 @@ Example for your character: > type/reset/force me = typeclasses.characters.Character -Examples: +### Usage + +#### Sdescs > look @@ -105,6 +107,14 @@ Tall man (assuming his name is Tom) sees: Note that by default, the case of the tag matters, so `/tall` will lead to 'tall man' while `/Tall` will become 'Tall man' and /TALL becomes /TALL MAN. If you don't want this behavior, you can pass case_sensitive=False to the `send_emote` function. +#### Language integration + +Speech can be identified as a particular language by prefixing it with the language key. + + emote says with a growl, orcish"Hello". + +This will identify the speech "Hello" as being spoken in orcish, and then pass that information on to `process_language` on your Character. By default, it doesn't do much, but you can hook in a language system such as the `rplanguage` module below to do more interesting things. + ## Language and whisper obfuscation system diff --git a/docs/source/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Making-A-Sittable-Object.md b/docs/source/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Making-A-Sittable-Object.md index 1223f48f21..d2266364e7 100644 --- a/docs/source/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Making-A-Sittable-Object.md +++ b/docs/source/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Making-A-Sittable-Object.md @@ -604,14 +604,13 @@ class CmdStand2(Command): key = "stand" def func(self): - - caller = self.caller - # if we are sitting, this should be set on us - sittable = caller.db.is_sitting - if not sittable: - caller.msg("You are not sitting down.") - else: - sittable.do_stand(caller) + caller = self.caller + # if we are sitting, this should be set on us + sittable = caller.db.is_sitting + if not sittable: + caller.msg("You are not sitting down.") + else: + sittable.do_stand(caller) ``` diff --git a/docs/source/api/evennia.contrib.base_systems.godotwebsocket.md b/docs/source/api/evennia.contrib.base_systems.godotwebsocket.md index 982aabbf47..90301cb944 100644 --- a/docs/source/api/evennia.contrib.base_systems.godotwebsocket.md +++ b/docs/source/api/evennia.contrib.base_systems.godotwebsocket.md @@ -13,6 +13,7 @@ evennia.contrib.base\_systems.godotwebsocket :maxdepth: 6 evennia.contrib.base_systems.godotwebsocket.test_text2bbcode + evennia.contrib.base_systems.godotwebsocket.test_webclient evennia.contrib.base_systems.godotwebsocket.text2bbcode evennia.contrib.base_systems.godotwebsocket.webclient diff --git a/docs/source/api/evennia.contrib.base_systems.godotwebsocket.test_webclient.md b/docs/source/api/evennia.contrib.base_systems.godotwebsocket.test_webclient.md new file mode 100644 index 0000000000..9199e7b46c --- /dev/null +++ b/docs/source/api/evennia.contrib.base_systems.godotwebsocket.test_webclient.md @@ -0,0 +1,10 @@ +```{eval-rst} +evennia.contrib.base\_systems.godotwebsocket.test\_webclient +=================================================================== + +.. automodule:: evennia.contrib.base_systems.godotwebsocket.test_webclient + :members: + :undoc-members: + :show-inheritance: + +``` \ No newline at end of file