evennia/docs/4.x/Contribs/Contrib-Godotwebsocket.html
2024-03-17 14:15:56 +01:00

329 lines
No EOL
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="generator" content="Docutils 0.17.1: http://docutils.sourceforge.net/" />
<title>Godot Websocket &#8212; Evennia latest documentation</title>
<link rel="stylesheet" href="../_static/nature.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<script id="documentation_options" data-url_root="../" src="../_static/documentation_options.js"></script>
<script src="../_static/jquery.js"></script>
<script src="../_static/underscore.js"></script>
<script src="../_static/doctools.js"></script>
<script src="../_static/language_data.js"></script>
<link rel="shortcut icon" href="../_static/favicon.ico"/>
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="next" title="Evennia in-game Python system" href="Contrib-Ingame-Python.html" />
<link rel="prev" title="Email-based login system" href="Contrib-Email-Login.html" />
</head><body>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../genindex.html" title="General Index"
accesskey="I">index</a></li>
<li class="right" >
<a href="../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Contrib-Ingame-Python.html" title="Evennia in-game Python system"
accesskey="N">next</a> |</li>
<li class="right" >
<a href="Contrib-Email-Login.html" title="Email-based login system"
accesskey="P">previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../index.html">Evennia latest</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="Contribs-Overview.html" accesskey="U">Contribs</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Godot Websocket</a></li>
</ul>
</div>
<div class="document">
<div class="documentwrapper">
<div class="sphinxsidebar" role="navigation" aria-label="main navigation">
<div class="sphinxsidebarwrapper">
<p class="logo"><a href="../index.html">
<img class="logo" src="../_static/evennia_logo.png" alt="Logo"/>
</a></p>
<div id="searchbox" style="display: none" role="search">
<h3 id="searchlabel">Quick search</h3>
<div class="searchformwrapper">
<form class="search" action="../search.html" method="get">
<input type="text" name="q" aria-labelledby="searchlabel" />
<input type="submit" value="Go" />
</form>
</div>
</div>
<script>$('#searchbox').show(0);</script>
<h3><a href="../index.html">Table of Contents</a></h3>
<ul>
<li><a class="reference internal" href="#">Godot Websocket</a><ul>
<li><a class="reference internal" href="#installation">Installation</a></li>
<li><a class="reference internal" href="#usage">Usage</a></li>
<li><a class="reference internal" href="#known-issues">Known Issues</a></li>
<li><a class="reference internal" href="#full-example-script">Full Example Script</a></li>
</ul>
</li>
</ul>
<h4>Previous topic</h4>
<p class="topless"><a href="Contrib-Email-Login.html"
title="previous chapter">Email-based login system</a></p>
<h4>Next topic</h4>
<p class="topless"><a href="Contrib-Ingame-Python.html"
title="next chapter">Evennia in-game Python system</a></p>
<div role="note" aria-label="source link">
<!--h3>This Page</h3-->
<ul class="this-page-menu">
<li><a href="../_sources/Contribs/Contrib-Godotwebsocket.md.txt"
rel="nofollow">Show Page Source</a></li>
</ul>
</div><h3>Links</h3>
<ul>
<li><a href="https://www.evennia.com/docs/latest/index.html">Documentation Top</a> </li>
<li><a href="https://www.evennia.com">Evennia Home</a> </li>
<li><a href="https://github.com/evennia/evennia">Github</a> </li>
<li><a href="http://games.evennia.com">Game Index</a> </li>
<li>
<a href="https://discord.gg/AJJpcRUhtF">Discord</a> -
<a href="https://github.com/evennia/evennia/discussions">Discussions</a> -
<a href="https://evennia.blogspot.com/">Blog</a>
</li>
</ul>
</div>
</div>
<div class="bodywrapper">
<div class="body" role="main">
<section class="tex2jax_ignore mathjax_ignore" id="godot-websocket">
<h1>Godot Websocket<a class="headerlink" href="#godot-websocket" title="Permalink to this headline"></a></h1>
<p>Contribution by ChrisLR, 2022</p>
<p>This contrib allows you to connect a Godot Client directly to your mud,
and display regular text with color in Godots RichTextLabel using BBCode.
You can use Godot to provide advanced functionality with proper Evennia support.</p>
<section id="installation">
<h2>Installation<a class="headerlink" href="#installation" title="Permalink to this headline"></a></h2>
<p>You need to add the following settings in your <a class="reference external" href="http://settings.py">settings.py</a> and restart evennia.</p>
<div class="highlight-python notranslate"><div class="highlight"><pre><span></span><span class="n">PORTAL_SERVICES_PLUGIN_MODULES</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s1">&#39;evennia.contrib.base_systems.godotwebsocket.webclient&#39;</span><span class="p">)</span>
<span class="n">GODOT_CLIENT_WEBSOCKET_PORT</span> <span class="o">=</span> <span class="mi">4008</span>
<span class="n">GODOT_CLIENT_WEBSOCKET_CLIENT_INTERFACE</span> <span class="o">=</span> <span class="s2">&quot;127.0.0.1&quot;</span>
</pre></div>
</div>
<p>This will make evennia listen on the port 4008 for Godot.
You can change the port and interface as you want.</p>
</section>
<section id="usage">
<h2>Usage<a class="headerlink" href="#usage" title="Permalink to this headline"></a></h2>
<p>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.</p>
<p>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 or look at the full example at the bottom of this page.</p>
<p><a class="reference external" href="https://docs.godotengine.org/en/stable/tutorials/networking/websocket.html">https://docs.godotengine.org/en/stable/tutorials/networking/websocket.html</a></p>
<p>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</p>
<p>A very basic setup in godot would require</p>
<ul class="simple">
<li><p>One RichTextLabel Node to display the Evennia Output, ensure bbcode is enabled on it.</p></li>
<li><p>One Node for your websocket client code with a new Script attached.</p></li>
<li><p>One TextEdit Node to enter commands</p></li>
<li><p>One Button Node to press and send the commands</p></li>
<li><p>Controls for the layout, in this example I have used
Panel
VBoxContainer
RichTextLabel
HBoxContainer
TextEdit
Button</p></li>
</ul>
<p>I will not go over how layout works but the documentation for them is easily accessible in the godot docs.</p>
<p>Open up the script for your client code.</p>
<p>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.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">extends</span> <span class="n">Node</span>
<span class="c1"># The URL we will connect to.</span>
<span class="n">var</span> <span class="n">websocket_url</span> <span class="o">=</span> <span class="s2">&quot;ws://localhost:4008&quot;</span>
<span class="n">var</span> <span class="n">socket</span> <span class="o">:=</span> <span class="n">WebSocketPeer</span><span class="o">.</span><span class="n">new</span><span class="p">()</span>
<span class="n">func</span> <span class="n">_ready</span><span class="p">():</span>
<span class="k">if</span> <span class="n">socket</span><span class="o">.</span><span class="n">connect_to_url</span><span class="p">(</span><span class="n">websocket_url</span><span class="p">)</span> <span class="o">!=</span> <span class="n">OK</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;Unable to connect.&quot;</span><span class="p">)</span>
<span class="n">set_process</span><span class="p">(</span><span class="n">false</span><span class="p">)</span>
<span class="n">func</span> <span class="n">_process</span><span class="p">(</span><span class="n">_delta</span><span class="p">):</span>
<span class="n">socket</span><span class="o">.</span><span class="n">poll</span><span class="p">()</span>
<span class="k">match</span> <span class="n">socket</span><span class="o">.</span><span class="n">get_ready_state</span><span class="p">():</span>
<span class="n">WebSocketPeer</span><span class="o">.</span><span class="n">STATE_OPEN</span><span class="p">:</span>
<span class="k">while</span> <span class="n">socket</span><span class="o">.</span><span class="n">get_available_packet_count</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="n">socket</span><span class="o">.</span><span class="n">get_packet</span><span class="p">()</span><span class="o">.</span><span class="n">get_string_from_ascii</span><span class="p">())</span>
<span class="n">WebSocketPeer</span><span class="o">.</span><span class="n">STATE_CLOSED</span><span class="p">:</span>
<span class="n">var</span> <span class="n">code</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">get_close_code</span><span class="p">()</span>
<span class="n">var</span> <span class="n">reason</span> <span class="o">=</span> <span class="n">socket</span><span class="o">.</span><span class="n">get_close_reason</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">&quot;WebSocket closed with code: </span><span class="si">%d</span><span class="s2">, reason </span><span class="si">%s</span><span class="s2">. Clean: </span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">[</span><span class="n">code</span><span class="p">,</span> <span class="n">reason</span><span class="p">,</span> <span class="n">code</span> <span class="o">!=</span> <span class="o">-</span><span class="mi">1</span><span class="p">])</span>
<span class="n">set_process</span><span class="p">(</span><span class="n">false</span><span class="p">)</span>
<span class="n">func</span> <span class="n">_exit_tree</span><span class="p">():</span>
<span class="n">socket</span><span class="o">.</span><span class="n">close</span><span class="p">()</span>
</pre></div>
</div>
<p>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, we will add a new function to dispatch the messages properly.</p>
<p>Here is an example</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">func</span> <span class="n">_handle_data</span><span class="p">(</span><span class="n">data</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="n">data</span><span class="p">)</span> <span class="c1"># Print for debugging</span>
<span class="n">var</span> <span class="n">data_array</span> <span class="o">=</span> <span class="n">JSON</span><span class="o">.</span><span class="n">parse_string</span><span class="p">(</span><span class="n">data</span><span class="p">)</span>
<span class="c1"># The first element can be used to see if its text</span>
<span class="k">if</span> <span class="n">data_array</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">==</span> <span class="s1">&#39;text&#39;</span><span class="p">:</span>
<span class="c1"># The second element contains the messages</span>
<span class="k">for</span> <span class="n">msg</span> <span class="ow">in</span> <span class="n">data_array</span><span class="p">[</span><span class="mi">1</span><span class="p">]:</span> <span class="n">write_to_rtb</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
<span class="n">func</span> <span class="n">write_to_rtb</span><span class="p">(</span><span class="n">msg</span><span class="p">):</span>
<span class="n">output_label</span><span class="o">.</span><span class="n">append_text</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
</pre></div>
</div>
<p>The first element is the type, it will be <code class="docutils literal notranslate"><span class="pre">text</span></code> if it is a message
It can be anything you would provide to the Evennia <code class="docutils literal notranslate"><span class="pre">msg</span></code> 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_text method.</p>
<p>If you want anything better than fancy text in Godot, you will have
to leverage Evennias OOB to send extra data.</p>
<p>You can <a class="reference external" href="https://www.evennia.com/docs/latest/OOB.html#oob">read more on OOB here</a>.</p>
<p>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.</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">func</span> <span class="n">_on_button_pressed</span><span class="p">():</span>
<span class="n">var</span> <span class="n">msg</span> <span class="o">=</span> <span class="n">text_edit</span><span class="o">.</span><span class="n">text</span>
<span class="n">var</span> <span class="n">msg_arr</span> <span class="o">=</span> <span class="p">[</span><span class="s1">&#39;text&#39;</span><span class="p">,</span> <span class="p">[</span><span class="n">msg</span><span class="p">],</span> <span class="p">{}]</span>
<span class="n">var</span> <span class="n">msg_str</span> <span class="o">=</span> <span class="n">JSON</span><span class="o">.</span><span class="n">stringify</span><span class="p">(</span><span class="n">msg_arr</span><span class="p">)</span>
<span class="n">socket</span><span class="o">.</span><span class="n">send_text</span><span class="p">(</span><span class="n">msg_str</span><span class="p">)</span>
<span class="n">text_edit</span><span class="o">.</span><span class="n">text</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span>
</pre></div>
</div>
</section>
<section id="known-issues">
<h2>Known Issues<a class="headerlink" href="#known-issues" title="Permalink to this headline"></a></h2>
<ul class="simple">
<li><p>Sending SaverDicts and similar objects straight from Evennia .DB will cause issues,
cast them to dict() or list() before doing so.</p></li>
</ul>
</section>
<section id="full-example-script">
<h2>Full Example Script<a class="headerlink" href="#full-example-script" title="Permalink to this headline"></a></h2>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>extends Node
# The URL we will connect to.
var websocket_url = &quot;ws://localhost:4008&quot;
var socket := WebSocketPeer.new()
@onready var output_label = $&quot;../Panel/VBoxContainer/RichTextLabel&quot;
@onready var text_edit = $&quot;../Panel/VBoxContainer/HBoxContainer/TextEdit&quot;
func _ready():
if socket.connect_to_url(websocket_url) != OK:
print(&quot;Unable to connect.&quot;)
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(&quot;WebSocket closed with code: %d, reason %s. Clean: %s&quot; % [code, reason, code != -1])
set_process(false)
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] == &#39;text&#39;:
# The second element contains the messages
for msg in data_array[1]: write_to_rtb(msg)
func write_to_rtb(msg):
output_label.append_text(msg)
func _on_button_pressed():
var msg = text_edit.text
var msg_arr = [&#39;text&#39;, [msg], {}]
var msg_str = JSON.stringify(msg_arr)
socket.send_text(msg_str)
text_edit.text = &quot;&quot;
func _exit_tree():
socket.close()
</pre></div>
</div>
<hr class="docutils" />
<p><small>This document page is generated from <code class="docutils literal notranslate"><span class="pre">evennia/contrib/base_systems/godotwebsocket/README.md</span></code>. Changes to this
file will be overwritten, so edit that file rather than this one.</small></p>
</section>
</section>
</div>
</div>
</div>
</div>
<div class="related" role="navigation" aria-label="related navigation">
<h3>Navigation</h3>
<ul>
<li class="right" style="margin-right: 10px">
<a href="../genindex.html" title="General Index"
>index</a></li>
<li class="right" >
<a href="../py-modindex.html" title="Python Module Index"
>modules</a> |</li>
<li class="right" >
<a href="Contrib-Ingame-Python.html" title="Evennia in-game Python system"
>next</a> |</li>
<li class="right" >
<a href="Contrib-Email-Login.html" title="Email-based login system"
>previous</a> |</li>
<li class="nav-item nav-item-0"><a href="../index.html">Evennia latest</a> &#187;</li>
<li class="nav-item nav-item-1"><a href="Contribs-Overview.html" >Contribs</a> &#187;</li>
<li class="nav-item nav-item-this"><a href="">Godot Websocket</a></li>
</ul>
</div>
<div class="admonition important">
<p class="first admonition-title">Note</p>
<p class="last">You are reading an old version of the Evennia documentation. <a href="https://www.evennia.com/docs/latest/index.html">The latest version is here</a></p>.
</div>
<div class="footer" role="contentinfo">
&#169; Copyright 2023, The Evennia developer community.
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 3.2.1.
</div>
</body>
</html>