<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://appsinacup.com//feed.xml" rel="self" type="application/atom+xml" /><link href="https://appsinacup.com//" rel="alternate" type="text/html" hreflang="en" /><updated>2026-03-21T13:30:28+01:00</updated><id>https://appsinacup.com//feed.xml</id><title type="html">Apps In A Cup</title><subtitle>Apps In A Cup of Coffee.</subtitle><author><name>Apps In A Cup</name><email>contact@appsinacup.com</email></author><entry><title type="html">Godot VSCode - Code inside the Godot Engine</title><link href="https://appsinacup.com//godot-vscode/" rel="alternate" type="text/html" title="Godot VSCode - Code inside the Godot Engine" /><published>2026-02-08T00:00:00+01:00</published><updated>2026-02-08T00:00:00+01:00</updated><id>https://appsinacup.com//godot-vscode</id><content type="html" xml:base="https://appsinacup.com//godot-vscode/"><![CDATA[<p><img controls="" loop="" autoplay="" muted="" style="width: 100%;" src="/assets/vid/vscode/vscode.gif" /></p>

<p><b>VSCode</b> running <i>inside</i> the Godot game engine.</p>

<p>How to get it? Download it from <a href="https://github.com/appsinacup/godot_vscode_ide">appsinacup/godot_vscode_ide</a>.</p>

<h2 id="implementation">Implementation</h2>

<p>For implementation, the <strong>VSCode</strong> Editor is actually a <strong>Webview</strong> rendering the page <code class="language-plaintext highlighter-rouge">https://vscode.dev</code>. When the Godot Engine starts, it runs the command: <code class="language-plaintext highlighter-rouge">code tunnel</code>. Then, the VSCode website connects to that.</p>

<p>This is done by using:</p>
<ul>
  <li>a modified version of <a href="https://github.com/doceazedo/godot_wry">doceazedo/godot_wry</a>, <a href="https://github.com/appsinacup/godot_wry">appsinacup/godot_wry</a> for the webview. Fixes a bunch of stuff in godot_wry for it to be able to run in editor, open popup links (For authentication), and more (also opened PR’s to fix them in mainstream repo)</li>
  <li>a custom written GDScript addon that creates and handles the vscode webpage.</li>
</ul>]]></content><author><name>dragos</name></author><category term="addon" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Polyglot Pirates - Alpha Test</title><link href="https://appsinacup.com//polyglot-pirates-alpha-test/" rel="alternate" type="text/html" title="Polyglot Pirates - Alpha Test" /><published>2026-02-01T00:00:00+01:00</published><updated>2026-02-01T00:00:00+01:00</updated><id>https://appsinacup.com//polyglot-pirates-alpha-test</id><content type="html" xml:base="https://appsinacup.com//polyglot-pirates-alpha-test/"><![CDATA[<video controls="" loop="" autoplay="" muted="" style="width: 100%;">
    <source type="video/webm" src="/assets/img/polyglot-pirates/polyglot.webm" />
</video>

<p>Hi all, the Alpha Test starts today! <strong>To participate, firstly</strong> you will need to complete the <a href="https://forms.gle/GpJ1rGwRWSsmnHcU9">Alpha Test Form</a> (if you haven’t already).</p>

<p><strong>Then</strong>, depending on the platform you want to test on:</p>
<ul>
  <li><strong>iPhone</strong>: You will need to go to the following <a href="https://testflight.apple.com/join/dHRXJPG5">Test Flight URL</a>.</li>
  <li><strong>Android</strong>: You will need to go to the following <a href="https://play.google.com/apps/internaltest/4701446401943780513">Google Play URL</a>.</li>
  <li><strong>Web</strong>: Go to <a href="https://appsinacup.itch.io/world">appsinacup.itch.io/world</a>.</li>
</ul>

<p>If you have questions, go on our <a href="https://discord.com/invite/v649emcpAu">Discord</a>.
After testing, fill in the following <a href="https://forms.gle/y2mRsra8zWCPxq4W7">Survey</a>.</p>

<h2 id="what-to-test">What to test?</h2>

<p>In this version we will have:</p>
<ul>
  <li><strong>Practice mode</strong>: Guess words without internet connection.</li>
  <li><strong>Challenge mode</strong>- Compete for highscore to see who can get the most words guessed in 1 minute.</li>
</ul>

<p>The following mini-games will be available:</p>
<ul>
  <li><strong>Word Match</strong>: Match words from base to target language.</li>
  <li><strong>Hangman</strong>: Guess letter by letter, with hint being in your base language.</li>
</ul>

<p>Fully <strong>translated languages</strong> available:</p>
<ul>
  <li>English, Romanian, Spanish, French, Chinese</li>
</ul>

<h2 id="known-issues">Known issues:</h2>

<ul>
  <li>[Web Builds] On Safari there might be some issues when authenticating. (Use for now Chromium based browsers or Firefox)</li>
  <li>[Chinese] On screen Pinyin keyboard doesn’t work. (Use for now the mobile keyboard instead)</li>
</ul>

<h2 id="what-will-be-next">What will be next?</h2>

<p>The upcoming month, we will implement:</p>
<ul>
  <li><strong>Story Mode</strong>: Levels, progression, unlocking words, etc.</li>
  <li><strong>Steam</strong>: Release for desktop</li>
  <li><strong>More Languages</strong>: Though this will depend also on the amount of translators we have.</li>
</ul>]]></content><author><name>dragos</name></author><category term="game" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Polyglot Pirates - Mission</title><link href="https://appsinacup.com//polyglot-pirates-mission/" rel="alternate" type="text/html" title="Polyglot Pirates - Mission" /><published>2026-01-17T00:00:00+01:00</published><updated>2026-01-17T00:00:00+01:00</updated><id>https://appsinacup.com//polyglot-pirates-mission</id><content type="html" xml:base="https://appsinacup.com//polyglot-pirates-mission/"><![CDATA[<video controls="" loop="" autoplay="" muted="" style="width: 100%;">
    <source type="video/webm" src="/assets/img/polyglot-pirates/polyglot.webm" />
</video>

<p><strong>Polyglot Pirates</strong> is a <strong>language learning</strong> multiplayer co-op game about pirates looking for their lost treasure.</p>

<p>The player must guide the pirate ship towards the treasure by <strong>matching words</strong>, playing <strong>hangman</strong> and <strong>finding words</strong> in a square of letters, all while trying to do it as <strong>quick as possible</strong>. The game will both work <strong>offline</strong> and <strong>online multiplayer</strong> (together with friends, <strong>even if</strong> they are <strong>learning another language</strong>).</p>

<p>We are targeting to have <strong>5000 words</strong> in <strong>30 languages</strong> (at launch). That’s a total of <strong>150.000</strong> words.</p>

<h1 id="mission">Mission</h1>

<p>The <strong>mission</strong> is to help people <strong>learn new languages</strong>, and motivate them to do so with <strong>competition</strong>. I strongly believe competition brings the best out of everyone.</p>

<h1 id="who-am-i">Who am I?</h1>

<p>I am <a href="https://github.com/Ughuuu">Dragos</a> and I have created the <a href="https://github.com/appsinacup">Apps In A Cup</a> group on GitHub, where for the last 3 years I have volunteered / contributed a lot of my free time to Open Source Software for Game Dev (<a href="https://github.com/appsinacup/godot-rapier-physics">Godot-Rapier</a>, <a href="https://github.com/appsinacup/godot-softbody2d">Godot-Softbody</a>, <a href="https://github.com/V-Sekai/godot-whisper">Godot-Whisper</a>, just to name a few) that are used by a lot of people. I have learned a great deal by doing so, and have started developing this game with 3 friends.</p>

<p>I wanted an <strong>alternative to Duolingo</strong> for language learning. I really like their main gameplay loop, however I <strong>hate their ads</strong>, <strong>energy system</strong>, <strong>AI translations</strong>, etc. I wanted also an <strong>European alternative</strong>.</p>

<h1 id="what-is-this-game">What is this game</h1>

<p>So, to keep this short, what I want out of this game are the following:</p>
<ul>
  <li><strong>No Ads</strong>: I want to learn here, not being served ads.</li>
  <li><strong>Offline Modes</strong>: Practice mode, Dictionary view, etc.</li>
  <li><strong>Multiplayer Modes</strong>: Competitive multiplayer modes, where the fastest wins. As well as tournaments organized in the weekends, etc.</li>
  <li><strong>Story Path</strong>: Complete it as you finish the arcade levels. Find out why the sea creatures stole your treasure and get it back</li>
  <li><strong>Customization</strong>: Unlock and customize your polyglot pirate.</li>
</ul>

<video controls="" loop="" autoplay="" muted="" style="width: 100%;">
    <source type="video/webm" src="/assets/img/polyglot-pirates-testing/word_guess_v2.webm" />
</video>

<video controls="" loop="" autoplay="" muted="" style="width: 100%;">
    <source type="video/webm" src="/assets/img/polyglot-pirates-testing/word_match_v2.webm" />
</video>

<h1 id="about-the-translations">About the translations</h1>

<p>The <strong>translations</strong> from the game are made available for free as <a href="https://opensource.org/license/mit">MIT licensed</a> (free to re-use even commercially). To get them go to this <a href="https://drive.google.com/drive/folders/1ulGtw6FFfwaIiLEqEeULWtQr5mWCT93Q">Google Drive Folder</a>. Anyone can contribute if they want, and they will get in exchange <strong>name credits</strong>.</p>]]></content><author><name>dragos</name></author><category term="game" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Polyglot Pirates - Register for Alpha Test</title><link href="https://appsinacup.com//polyglot-alpha-phase/" rel="alternate" type="text/html" title="Polyglot Pirates - Register for Alpha Test" /><published>2026-01-16T00:00:00+01:00</published><updated>2026-01-16T00:00:00+01:00</updated><id>https://appsinacup.com//polyglot-alpha-phase</id><content type="html" xml:base="https://appsinacup.com//polyglot-alpha-phase/"><![CDATA[<video controls="" loop="" autoplay="" muted="" style="width: 100%;">
    <source type="video/webm" src="/assets/img/polyglot-pirates/polyglot.webm" />
</video>

<p><strong>Polyglot Pirates</strong> will start the <strong>Alpha Testing</strong> phase on <strong>1st of Feb</strong>. It will be a language learning multiplayer co-op game about pirates looking for their lost treasure.</p>

<ul>
  <li><a href="https://docs.google.com/forms/d/e/1FAIpQLSdlQ8G2K8xzFseLxhx-rBCh65hYDMv_47sQssHWdpu5P7ELzA/viewform">Registration form</a>: If you want to register for the upcoming alpha.</li>
</ul>

<h2 id="features">Features</h2>

<p>In the <strong>Alpha Version</strong> of the game will have the following:</p>

<ul>
  <li><strong>Practice mode</strong> lets you practice without the stress of the levels (without progression) and it will work offline also.</li>
  <li><strong>Story path</strong> to complete as you finish the arcade levels. Find out why the sea creatures stole your treasure and get it back!</li>
  <li><strong>Watch the leaderboards</strong> to see your best score while competing with other learners around the world.</li>
  <li><strong>Read the dictionary</strong> grouped by categories and subcategories even when offline.</li>
  <li><strong>Customize with accessories</strong> the main player, that are unlocked by playing.</li>
</ul>

<video controls="" loop="" autoplay="" muted="" style="width: 100%;">
    <source type="video/webm" src="/assets/img/polyglot-pirates-testing/word_guess_v2.webm" />
</video>

<video controls="" loop="" autoplay="" muted="" style="width: 100%;">
    <source type="video/webm" src="/assets/img/polyglot-pirates-testing/word_match_v2.webm" />
</video>

<p>Sometime in march, in the <strong>Beta Version</strong>, there will be the following features:</p>
<ul>
  <li><strong>Play together</strong> by inviting friends in the same level.</li>
  <li><strong>5000 words per language</strong> (still work in progress). Planned at release is to have 30 languages.</li>
</ul>

<h2 id="links">Links</h2>

<ul>
  <li><a href="https://discord.com/invite/v649emcpAu">Discord Community</a></li>
  <li><a href="https://polyglotpirates.com">PolyglotPirates.com</a></li>
</ul>

<h2 id="more-info">More Info</h2>

<p>The <strong>translations</strong> from the game are made available for free as <a href="https://opensource.org/license/mit">MIT licensed</a> (free to re-use even commercially). To get them go to this <a href="https://drive.google.com/drive/folders/1ulGtw6FFfwaIiLEqEeULWtQr5mWCT93Q">Google Drive Folder</a>. The contributors will be given in exchange <strong>name credits</strong>. Simply say in the write access request what name or pseudoname you want credited.</p>]]></content><author><name>dragos</name></author><category term="game" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Polyglot Pirates - Translations</title><link href="https://appsinacup.com//polyglot-translators-update/" rel="alternate" type="text/html" title="Polyglot Pirates - Translations" /><published>2026-01-12T00:00:00+01:00</published><updated>2026-01-12T00:00:00+01:00</updated><id>https://appsinacup.com//polyglot-translators-update</id><content type="html" xml:base="https://appsinacup.com//polyglot-translators-update/"><![CDATA[<p><img src="/assets/img/polyglot-pirates-testing/banner.png" alt="polyglot logo" /></p>

<p><strong>Polyglot Pirates</strong> is an upcoming language learning game about pirates looking for their lost treasure.</p>

<ul>
  <li><a href="https://discord.com/invite/v649emcpAu">Discord Community</a></li>
  <li><a href="https://polyglotpirates.com">PolyglotPirates.com</a></li>
</ul>

<p>The translations from the game are made available for free as <a href="https://opensource.org/license/mit">MIT licensed</a> (free to re-use even commercially). To get them go to this <a href="https://drive.google.com/drive/folders/1ulGtw6FFfwaIiLEqEeULWtQr5mWCT93Q">Google Drive Folder</a>.</p>

<p>The game will have <strong>30 languages</strong> translated by us or contributors: Arabic, Bulgarian, Czech, Danish, German, Greek, English, Spanish (Spain), Spanish (Latin America), Finnish, French, Hungarian, Indonesian, Italian, Japanese, Korean, Dutch, Norwegian, Polish, Portuguese, Portuguese (Brazil), Romanian, Russian, Swedish, Thai, Turkish, Ukrainian, Vietnamese, Chinese (Simplified), Chinese (Traditional).</p>

<p>The contributors will be given in exchange <strong>name credits</strong>. Simply say in the write access request what name or pseudoname you want credited.</p>

<p>Words are grouped in <strong>categories and subcategories</strong>, and contain part of speech, eg.:</p>
<ul>
  <li>People -&gt; Body -&gt; head (noun)</li>
</ul>

<p>In total there are about <strong>5000 words</strong> selected for dictionaries, as well as some used in game for labels and buttons.</p>

<p>If you are interested to <strong>contribute</strong>, simply go to one of the dictionary sheets and apply for write access.</p>

<p>Below are some visuals of our upcoming game:</p>

<video controls="" loop="" autoplay="" muted="" style="width: 100%;">
    <source type="video/webm" src="/assets/img/polyglot-pirates-testing/word_guess_v2.webm" />
</video>

<video controls="" loop="" autoplay="" muted="" style="width: 100%;">
    <source type="video/webm" src="/assets/img/polyglot-pirates-testing/word_match_v2.webm" />
</video>

<p>And lastly, if you want to register for our upcoming alpha test, go here: <a href="https://docs.google.com/forms/d/e/1FAIpQLSdlQ8G2K8xzFseLxhx-rBCh65hYDMv_47sQssHWdpu5P7ELzA/viewform">Polyglot Testers</a></p>]]></content><author><name>dragos</name></author><category term="game" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Gamend - LibGodot Integration</title><link href="https://appsinacup.com//gamend-libgodot-integration/" rel="alternate" type="text/html" title="Gamend - LibGodot Integration" /><published>2026-01-02T00:00:00+01:00</published><updated>2026-01-02T00:00:00+01:00</updated><id>https://appsinacup.com//gamend-libgodot-integration</id><content type="html" xml:base="https://appsinacup.com//gamend-libgodot-integration/"><![CDATA[<p><img src="/assets/img/gamend/banner.png" alt="banner" /></p>

<p>Previously I had added integration with Godot from Gamend:</p>
<ul>
  <li>Host (Elixir) -&gt; Godot (WebSocket)</li>
</ul>

<h2 id="the-problem">The Problem</h2>

<p>This was nice as a proof of concept, but in real life, it was <strong>slow</strong>:</p>

<table>
  <thead>
    <tr>
      <th>Time</th>
      <th>Case</th>
      <th>Language</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>0.1 - 0.5 ms</td>
      <td>Simple Function</td>
      <td>Elixir</td>
    </tr>
    <tr>
      <td>0.5 - 1 ms</td>
      <td>Complex Function</td>
      <td>Elixir</td>
    </tr>
    <tr>
      <td>1-10 ms</td>
      <td>Simple Function</td>
      <td>GDScript</td>
    </tr>
    <tr>
      <td>~100 ms</td>
      <td>Complex Function</td>
      <td>GDScript</td>
    </tr>
  </tbody>
</table>

<p>The <strong>Simple Function</strong> is a HTTP call with Authentication, while the <strong>Complex Function</strong> one does also database access or cache access.</p>

<p>In <strong>Godot</strong> it is so slow because the communication layer is WebSocket, and whenever Godot calls back into Host, it does so with HTTP. For a simple case, it’s just the WebSocket overhead:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Gamend --WS-&gt; Godot --WS-&gt; Gamend
</code></pre></div></div>

<p>For a more complex case, it is both the WebSocket and the HTTP layer from Gamend:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Gamend
| (Send input)
WS
v
Godot -HTTP-&gt; Gamend (eg. create KV)
      -HTTP-&gt; Gamend (eg. get KV)
| (Receive result)
WS
v
Gamend
</code></pre></div></div>

<p>The delay added from both WS (WebSocket) and HTTP (calling back into the server for Admin SDK) is too big to make it feasible.</p>

<h2 id="libgodot">LibGodot</h2>

<p>LibGodot is an <a href="https://godotengine.org/article/dev-snapshot-godot-4-6-dev-2/#build-godot-engine-as-a-library">upcoming feature</a> of Godot where one can now build the engine as a shared library (or static), and then start the engine from within the host application.</p>

<p>Until that ships, I cloned locally <a href="https://github.com/migeran/libgodot">migeran/libgodot</a> (which uses Godot 4.3 with some changes) and after some time of building both <strong>godot</strong> and <strong>godot-cpp</strong>, I built the cpp-sample and ran it:</p>

<p><img src="/assets/img/libgodot/cpp-sample.png" alt="libgodot" /></p>

<div class="language-cpp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span> <span class="o">**</span><span class="n">argv</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">LibGodot</span> <span class="n">libgodot</span><span class="p">;</span>

    <span class="n">godot</span><span class="o">::</span><span class="n">GodotInstance</span> <span class="o">*</span><span class="n">instance</span> <span class="o">=</span> <span class="n">libgodot</span><span class="p">.</span><span class="n">create_godot_instance</span><span class="p">(</span><span class="n">argc</span><span class="p">,</span> <span class="n">argv</span><span class="p">);</span>

    <span class="n">instance</span><span class="o">-&gt;</span><span class="n">start</span><span class="p">();</span>
    <span class="k">while</span> <span class="p">(</span><span class="o">!</span><span class="n">instance</span><span class="o">-&gt;</span><span class="n">iteration</span><span class="p">())</span> <span class="p">{}</span>
    <span class="n">libgodot</span><span class="p">.</span><span class="n">destroy_godot_instance</span><span class="p">(</span><span class="n">instance</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>How it works? It starts godot and calls <strong>iteration</strong> as to move the engine <strong>process</strong> function forward.</p>

<h2 id="libgodot-in-gamend">LibGodot in Gamend</h2>

<p>I extended this functionality and sample to have a function for sending data <strong>to Godot</strong>, and one where Godot can send data back <strong>to Elixir</strong> (Gamend).</p>

<h3 id="about-the-main-thread">About the main thread</h3>

<p>Since Elixir runs it’s application inside BEAM and that doesn’t offer access to the <strong>main thread</strong> (and Godot really wants to run on the main thread), I ended up starting a thread and running there Godot. That means Godot won’t be able to load menus (and possibly other stuff) that require main thread. This is fine for my use cases, as I run it <strong>headless</strong> anyway.</p>

<h3 id="data-bus">Data Bus</h3>

<p>As an initial API, I made a Data Bus for <strong>sending and receiving data</strong> from and to Elixir from Godot.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">extends</span> <span class="nx">MeshInstance3D</span>

<span class="kd">var</span> <span class="nx">bus</span>

<span class="nx">func</span> <span class="nx">_ready</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="k">void</span><span class="p">:</span>
	<span class="nx">bus</span> <span class="o">=</span> <span class="nx">Engine</span><span class="p">.</span><span class="nx">get_singleton</span><span class="p">(</span><span class="dl">"</span><span class="s2">ElixirBus</span><span class="dl">"</span><span class="p">)</span>
	<span class="k">if</span> <span class="nx">bus</span><span class="p">:</span>
		<span class="nx">bus</span><span class="p">.</span><span class="nx">connect</span><span class="p">(</span><span class="dl">"</span><span class="s2">new_message</span><span class="dl">"</span><span class="p">,</span> <span class="nx">Callable</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="dl">"</span><span class="s2">_on_new_message</span><span class="dl">"</span><span class="p">))</span>
		<span class="nx">bus</span><span class="p">.</span><span class="nx">send_event</span><span class="p">(</span><span class="dl">"</span><span class="s2">status</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">godot_ready</span><span class="dl">"</span><span class="p">)</span>

<span class="nx">func</span> <span class="nx">_on_new_message</span><span class="p">(</span><span class="nx">_queued</span><span class="p">:</span> <span class="nx">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="k">void</span><span class="p">:</span>
  <span class="k">for</span> <span class="nx">msg</span> <span class="k">in</span> <span class="nx">bus</span><span class="p">.</span><span class="nx">drain</span><span class="p">():</span>
    <span class="nx">bus</span><span class="p">.</span><span class="nx">send_event</span><span class="p">(</span><span class="dl">"</span><span class="s2">request_handled</span><span class="dl">"</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span>
    <span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">Elixir message: </span><span class="dl">"</span><span class="p">,</span> <span class="nx">msg</span><span class="p">)</span>
  
<span class="nx">func</span> <span class="nx">_process</span><span class="p">(</span><span class="nx">delta</span><span class="p">):</span>
  <span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">A</span><span class="dl">"</span><span class="p">)</span>
  <span class="nx">print</span><span class="p">(</span><span class="nx">rotation</span><span class="p">)</span>
	<span class="nx">rotate_x</span><span class="p">(</span><span class="nx">delta</span><span class="p">)</span>
</code></pre></div></div>

<h2 id="conclusion">Conclusion</h2>

<p>And it works:</p>

<p><img src="/assets/img/libgodot/gdscript.png" alt="gdscript" /></p>

<p>And the time is much better than before, in order of 0.1 - 0.5 ms, closer to what Elixir was obtaining:</p>

<p><img src="/assets/img/libgodot/timing.png" alt="timing" /></p>]]></content><author><name>dragos</name></author><category term="game" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Gamend - Godot Integration</title><link href="https://appsinacup.com//gamend-godot-integration/" rel="alternate" type="text/html" title="Gamend - Godot Integration" /><published>2025-12-31T00:00:00+01:00</published><updated>2025-12-31T00:00:00+01:00</updated><id>https://appsinacup.com//gamend-godot-integration</id><content type="html" xml:base="https://appsinacup.com//gamend-godot-integration/"><![CDATA[<p><img src="/assets/img/gamend/banner.png" alt="banner" /></p>

<p>Gamend now has a module that connects to <strong>Godot</strong> so it’s possible to write server side logic in <strong>GDScript</strong>.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  +--------------------+
  | GameServer/Gamend  |
  +--------------------+
            |
            | calls hooks (WebSocket)
            | 
            v
  +--------------------+
  | godot_hook plugin  |
  | (spawns Godot)     |
  | ( WS client)       |
  +--------------------+
            |
            | sends and receives hooks through Websocket Client
            v
  +--------------------+
  | Godot headless     |
  | (starts WS server) |
  +--------------------+
</code></pre></div></div>

<p>The <a href="https://github.com/appsinacup/gamend_starter/tree/main/modules/plugins/godot_hook">godot_hook</a> is available in the gamend_starter repo. The Dockerfile <strong>downloads Godot</strong>, and then the godot_hook starts it and <strong>forwards hook events</strong>.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">class_name</span> <span class="nx">GamendServerTest</span>
<span class="kd">extends</span> <span class="nx">GamendServer</span>

<span class="err">#</span> <span class="nx">Callbacks</span>
<span class="nx">func</span> <span class="nx">_after_user_login</span><span class="p">(</span><span class="nx">user</span><span class="p">:</span> <span class="nx">Dictionary</span><span class="p">):</span>
	<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">after_user_login: </span><span class="dl">"</span><span class="p">,</span> <span class="nx">user</span><span class="p">)</span>

<span class="nx">func</span> <span class="nx">_after_startup</span><span class="p">():</span>
	<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">after_startup</span><span class="dl">"</span><span class="p">)</span>

<span class="err">#</span> <span class="nx">RPC</span>
<span class="nx">func</span> <span class="nx">new_func</span><span class="p">(</span><span class="nx">arg</span><span class="p">:</span> <span class="nb">String</span><span class="p">):</span>
	<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">new_func: </span><span class="dl">"</span><span class="p">,</span> <span class="nx">arg</span><span class="p">)</span>
	<span class="k">return</span> <span class="nx">arg</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">abc</span><span class="dl">"</span>
</code></pre></div></div>

<p>Callbacks are forwarded from the server, and you can also expose new functions that the user can then call from client side. There is also a new <strong>admin SDK</strong> so everything that is possible from Elixir is also possible from Godot (autogenerated with OpenAPI).</p>

<p>Eg.</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">gamend_api</span><span class="p">:</span> <span class="nx">GamendApi</span>

<span class="nx">func</span> <span class="nx">_init</span><span class="p">():</span>

<span class="nx">func</span> <span class="nx">_ready</span><span class="p">():</span>
  <span class="nx">gamend_api</span> <span class="o">=</span> <span class="nx">GamendApi</span><span class="p">.</span><span class="k">new</span><span class="p">(</span><span class="dl">"</span><span class="s2">localhost</span><span class="dl">"</span><span class="p">,</span> <span class="mi">4000</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span>
	<span class="nx">add_child</span><span class="p">(</span><span class="nx">gamend_api</span><span class="p">)</span>
  <span class="nx">_get_kv</span><span class="p">()</span>

<span class="nx">func</span> <span class="nx">_get_kv</span><span class="p">():</span>
	<span class="kd">var</span> <span class="nx">result</span> <span class="o">=</span> <span class="k">await</span> <span class="nx">gamend_api</span><span class="p">.</span><span class="nx">kv_get_kv</span><span class="p">(</span><span class="dl">"</span><span class="s2">my_data</span><span class="dl">"</span><span class="p">)</span>
	<span class="k">if</span> <span class="nx">result</span><span class="p">.</span><span class="nx">error</span><span class="p">:</span>
		<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">Cannot get KV at my_data</span><span class="dl">"</span><span class="p">)</span>
		<span class="k">return</span> <span class="nx">Dictionary</span><span class="p">()</span>
	<span class="nx">print</span><span class="p">(</span><span class="dl">"</span><span class="s2">KV is: </span><span class="dl">"</span><span class="p">,</span> <span class="nx">result</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">)</span>
</code></pre></div></div>]]></content><author><name>dragos</name></author><category term="game" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Gamend - Game Server Deploy</title><link href="https://appsinacup.com//gamend-deploy/" rel="alternate" type="text/html" title="Gamend - Game Server Deploy" /><published>2025-12-30T00:00:00+01:00</published><updated>2025-12-30T00:00:00+01:00</updated><id>https://appsinacup.com//gamend-deploy</id><content type="html" xml:base="https://appsinacup.com//gamend-deploy/"><![CDATA[<p><img src="/assets/img/gamend/banner.png" alt="banner" /></p>

<p>This tutorial will help you to deploy Gamend using <a href="https://fly.io">fly.io</a>. For this we are using a shared instance that will cost about <strong>5$ per month</strong> (shared-cpu-2x with 512MB) (<a href="https://fly.io/docs/about/pricing/">Fly.io pricing</a>):</p>

<p><img src="/assets/img/gamend_deploy/0-cost.png" alt="cost" /></p>

<h2 id="create-an-account">Create an account</h2>
<ul>
  <li>Create an account on <a href="https://fly.io">fly.io</a>.</li>
  <li>Install locally <a href="https://fly.io/docs/flyctl/install/">flyctl</a>:
    <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># macOS</span>
brew <span class="nb">install </span>flyctl
<span class="c"># Linux</span>
curl <span class="nt">-L</span> https://fly.io/install.sh | sh
<span class="c"># Windows</span>
pwsh <span class="nt">-Command</span> <span class="s2">"iwr https://fly.io/install.ps1 -useb | iex"</span>
</code></pre></div>    </div>
  </li>
</ul>

<h2 id="configure">Configure</h2>

<ul>
  <li>Clone/fork the <a href="https://github.com/appsinacup/gamend_starter">gamend_starter</a> repo.</li>
  <li>Run in the repo the following command:
    <div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>fly deploy
</code></pre></div>    </div>
  </li>
</ul>

<h2 id="deploy">Deploy</h2>

<ul>
  <li>Select <code class="language-plaintext highlighter-rouge">y</code> on the re-use fly.io (first prompt), then press Enter on other questions:
<img src="/assets/img/gamend_deploy/1-launch.png" alt="banner" /></li>
  <li>That’s it, at the end you will be given a URL you can visit to see the app:
<img src="/assets/img/gamend_deploy/2-view.png" alt="view" /></li>
  <li>Go to the URL and Register using Email (first account created will be Admin).
<img src="/assets/img/gamend_deploy/3-web.png" alt="web" /></li>
</ul>

<h2 id="notes">Notes:</h2>

<p>This starter uses <strong>dev mode</strong> and has nothing configured. If you want to do a real deployment, configure secrets in the fly.io Secrets tab (eg. OAuth, Email server, etc.)</p>

<p><img src="/assets/img/gamend_deploy/4-secrets.png" alt="4-secrets" /></p>

<p>Also, this is a simple deployment with 1 instance, SQLite and local caching. For more complex deployments, see the <a href="https://appsinacup.com/gamend-scaling/">Scaling article</a>.</p>]]></content><author><name>dragos</name></author><category term="game" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Gamend - Game Server Scaling</title><link href="https://appsinacup.com//gamend-scaling/" rel="alternate" type="text/html" title="Gamend - Game Server Scaling" /><published>2025-12-29T00:00:00+01:00</published><updated>2025-12-29T00:00:00+01:00</updated><id>https://appsinacup.com//gamend-scaling</id><content type="html" xml:base="https://appsinacup.com//gamend-scaling/"><![CDATA[<p><img src="/assets/img/gamend/banner.png" alt="banner" /></p>

<p>Both the running instance of gamend, which can be found at <a href="https://gamend.appsinacup.com">https://gamend.appsinacup.com</a> and the <a href="https://github.com/appsinacup/gamend_starter?tab=readme-ov-file#multi-node-local-deployment">README.md</a> now have information about how to deploy the app at scale.</p>

<h2 id="infrastructure">Infrastructure</h2>

<p>When scaling, the recommended is to use Postgres database and Redis cache:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>+------------------------------+
|   Gamend <span class="o">(</span>App Instance 1<span class="o">)</span>    |
+------------------------------+
    	  |           |
    	  |Database   |Cache
    	  v           v
    +----------+   +-------+
    | Postgres |   | Redis |
    +----------+   +-------+
    	  ^           ^
    	  |Database   |Cache
    	  |           |
+------------------------------+
|   Gamend <span class="o">(</span>App Instance 2<span class="o">)</span>    |
+------------------------------+

<span class="c"># Etc. to n instances</span>
</code></pre></div></div>

<h2 id="tutorial-docker-compose">Tutorial (Docker Compose)</h2>

<ol>
  <li>Clone the <a href="https://github.com/appsinacup/gamend_starter">gamend_starter</a> repo</li>
  <li>Run the following:</li>
</ol>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker compose <span class="nt">-f</span> docker-compose.multi.yml up <span class="nt">--scale</span> <span class="nv">app</span><span class="o">=</span>2
</code></pre></div></div>

<p>This will start 2 instances of the <code class="language-plaintext highlighter-rouge">gamend</code> app, a Postgres database, Redis cache and a nginx that acts as a load balancer.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>- gamend_starter
  - nginx-1: Ports 4000:80
  - db-1: Ports 5432:5432
  - redis-1: Ports  6379:6379
  - app-1
  - app-2
</code></pre></div></div>

<p>Now, go to the browser to <a href="http://localhost:4000">http://localhost:4000</a>, register with any email and then go to the dev mailbox, at <a href="http://localhost:4000/dev/mailbox">http://localhost:4000/dev/mailbox</a> (Only enabled locally to test, in prod disable it). Accept the registration, then login with magic link. You should now have admin access (first registered user is admin).</p>

<h3 id="load-balancer">Load Balancer</h3>

<p>The nginx load balancer will call into either <code class="language-plaintext highlighter-rouge">app-1</code> or <code class="language-plaintext highlighter-rouge">app-2</code>. The gamend server has a cache layer, which can be configured in different ways, but for this it’s configured with Redis. The database of choice is Postgres here (for single deployment it can also use SQLite).</p>

<h3 id="multilevel-cache">Multilevel Cache</h3>

<p>The app uses 2 level caching. First <code class="language-plaintext highlighter-rouge">local in memory</code>, and then <code class="language-plaintext highlighter-rouge">Redis</code> (over network). When something is written to the database or read, the cache is updated both locally and distributed. Read more about Multilevel cache here:</p>
<ul>
  <li><a href="https://hexdocs.pm/nebulex/3.0.0-rc.2/getting-started.html#multilevel-cache">Nebulex Multilevel Cache</a></li>
</ul>]]></content><author><name>dragos</name></author><category term="game" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">Polyglot Pirates - Guide Document</title><link href="https://appsinacup.com//polyglot-pirates-guide-document/" rel="alternate" type="text/html" title="Polyglot Pirates - Guide Document" /><published>2025-12-20T00:00:00+01:00</published><updated>2025-12-20T00:00:00+01:00</updated><id>https://appsinacup.com//polyglot-pirates-guide-document</id><content type="html" xml:base="https://appsinacup.com//polyglot-pirates-guide-document/"><![CDATA[<p><img src="/assets/img/polyglot-pirates/icon_nobg.png" alt="polyglot logo" /></p>

<ol>
  <li><a href="#1-login-flow">Login Flow</a>
    <ul>
      <li>1.1 <a href="#11-login-page">Login Page</a></li>
      <li>1.2 <a href="#12-first-time-page">First Time Page</a></li>
    </ul>
  </li>
  <li><a href="#2-main-menu-flow">Main Menu Flow</a>
    <ul>
      <li>2.1 <a href="#21-island-page">Island Page</a></li>
      <li>2.2 <a href="#22-settings-page">Settings Page</a></li>
      <li>2.3 <a href="#23-profile-page">Profile Page</a></li>
      <li>2.4 <a href="#24-friends-page">Friends Page</a></li>
      <li>2.5 <a href="#25-dictionaries-page">Dictionaries Page</a></li>
      <li>2.6 <a href="#26-leaderboards-page">Leaderboards Page</a></li>
      <li>2.7 <a href="#27-ship-page">Ship Page</a></li>
    </ul>
  </li>
</ol>

<h2 id="bug-reporting">Bug Reporting</h2>

<p>If on any of the flows you find either an edge case that is not yet treated or a bug, report it so it can be fixed (on our <a href="https://discord.com/invite/v649emcpAu">Discord</a> Page). Thanks in advance.</p>

<h1 id="1-login-flow">1. Login Flow</h1>

<p>Logging into the app can be done either by using OAuth (eg. Apple/Google) or by using Device Auth.</p>

<p>If you are already logged in, you can Logout by going to Settings -&gt; Logout or Delete (scroll down). Logging out will still retain your user profile (eg. configuration/language selection), while Delete will delete your user account so you can start from scratch. Both of these have a confirmation window.</p>

<h2 id="11-login-page">1.1 Login Page</h2>

<p>This page is only visible if you are not logged in. After you login, the login should be cached for at least 30 days.</p>

<ul>
  <li>Change the Language.</li>
  <li>Login with Apple/Google/etc. (Depends on mobile) or continue as Guest (on web not available).</li>
</ul>

<p><img src="/assets/img/polyglot-pirates-testing/LoginScreen.png" alt="Login Screen" /></p>

<h1 id="12-first-time-page">1.2 First Time Page</h1>

<p>If you already configured your target language, this page will not show up anymore.</p>

<ul>
  <li>Configure your Name</li>
  <li>Change the Language</li>
  <li>Configure your Target Language (what language you want to learn)</li>
  <li>Continue button: disabled until the Target Language is set.</li>
</ul>

<p><img src="/assets/img/polyglot-pirates-testing/InitialScreen.png" alt="Initial Screen" /></p>

<h1 id="13-initial-animation">1.3 Initial Animation</h1>

<p>The initial animation happens after First Time Page (or Login Page if First Time is skipped). It is skipped if the player is in a Game. If you want to replay the Initial Animation, Restart the game.</p>

<ul>
  <li>Can be sped up by holding mouse down</li>
  <li>Can be skipped by going to Settings -&gt; Skip Introduction</li>
</ul>

<video controls="" loop="" autoplay="" muted="" style="height: 100vh">
    <source type="video/webm" src="/assets/img/polyglot-pirates-testing/IntroAnimation.webm" />
</video>

<h1 id="2-main-menu-flow">2. Main Menu Flow</h1>

<p>After logging in and seeing the initial animation (or skipping it) you arrive to the main menu. In this flow, you are on the island and you can customise the player or game settings, as well as see various information (eg. leaderboards, dictionaries, friends, etc.)</p>

<h1 id="21-island-page">2.1 Island Page</h1>

<p>The Island page offers information about the player, the ship, and more submenus.</p>

<ul>
  <li>On landscape, the menus should be on the right (resize the window or rotate device)</li>
  <li>On portrait, the menus should be on the bottom (resize the window or rotate device)</li>
  <li>You should see the Menu Buttons (Settings, Personalize, Friends, Dictionaries, Leaderboards) and Game Buttons (Start)</li>
</ul>

<table>
  <thead>
    <tr>
      <th> </th>
      <th> </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><img src="/assets/img/polyglot-pirates-testing/menu_right.png" alt="menu_right" /></td>
      <td><img src="/assets/img/polyglot-pirates-testing/menu_bottom.png" alt="menu_bottom" /></td>
    </tr>
  </tbody>
</table>

<h1 id="22-settings-page">2.2 Settings Page</h1>

<p>On the Settings page you can update game settings, such as language, sound, gameplay, account, as well as view community information.</p>

<ul>
  <li>When changing Language, the UI language should update. It should also persist between restarts.</li>
  <li>When changing Target Language, the target language (in game) should update. It should also persist between restarts.</li>
  <li>When changing Sound Effects Volume, the sound effects volume should update. It should also persist between restarts.</li>
  <li>When changing Music Volume, the music volume should update. It should also persist between restarts.</li>
  <li>When Disabling Audio, the music and sound effects should be muted. It should also persist between restarts.</li>
  <li>When Show Next Word is active, in minigames you should see next word and current word. It should also persist between restarts.</li>
  <li>When Skip Introduction is active, the intro animation will be skipped on game restarts. It should also persist between restarts.</li>
  <li>When clicking on Feedback, you should be presented with a Feedback Form.</li>
  <li>When clicking on Discord, a link to this Discord Community should open in the browser.</li>
  <li>When clicking to Link/Unlink an account, you should be guided through the Login flow, and if successful the account will be linked, if not an error will be shown.</li>
  <li>When clicking the Logout button, your session will be deleted, and the game will go back to Login Screen. All device settings done so far will also be deleted.</li>
  <li>When clicking the Delete button, your account will be deleted, and the game will go back to Login Screen. All device settings done so far will also be deleted. All user settings from the server will also be deleted</li>
</ul>

<p><img src="/assets/img/polyglot-pirates-testing/SettingsScreen.png" alt="SettingsScreen" /></p>

<h1 id="23-profile-page">2.3 Profile Page</h1>

<p>On the Profile page you can personalize your character.</p>

<ul>
  <li>Choose a name</li>
  <li>Choose a color. The color should update your character color</li>
  <li>Choose a hat. The hat should update your character hat</li>
  <li>Choose an accessory. The accessory should update your character accessory</li>
</ul>

<p><img src="/assets/img/polyglot-pirates-testing/profile_screen.png" alt="profile_screen" /></p>

<h1 id="24-friends-page">2.4 Friends Page</h1>

<p>On the Friends page you can view/accept/reject friends. This page has 3 tabs:</p>

<h2 id="friends-tab">Friends Tab</h2>

<ul>
  <li>Search users by display name.</li>
  <li>Send friend requests.</li>
  <li>Remove existing friends.</li>
</ul>

<p><img src="/assets/img/polyglot-pirates-testing/friend.png" alt="friend" /></p>

<h2 id="received-tab">Received Tab</h2>

<ul>
  <li>Accept or decline friend requests.</li>
</ul>

<p><img src="/assets/img/polyglot-pirates-testing/request.png" alt="request" /></p>

<h2 id="sent-tab">Sent Tab</h2>

<ul>
  <li>View or revoke sent friend requests.</li>
</ul>

<p><img src="/assets/img/polyglot-pirates-testing/sent.png" alt="request" /></p>

<h1 id="25-dictionaries-page">2.5 Dictionaries Page</h1>

<p>In the Dictionaries Page you will be able to see the dictionaries and the progress you have on them, as well as select a dictionary and view words inside it.</p>

<h2 id="dictionary-tab">Dictionary Tab</h2>
<ul>
  <li>View Dictionaries</li>
</ul>

<h2 id="word-tab">Word Tab</h2>
<ul>
  <li>View Words</li>
  <li>Go back to Dictionaries Tab</li>
</ul>

<table>
  <thead>
    <tr>
      <th> </th>
      <th> </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><img src="/assets/img/polyglot-pirates-testing/dictionaries_screen.png" alt="dictionaries_screen" /></td>
      <td><img src="/assets/img/polyglot-pirates-testing/words_screen.png" alt="words_screen" /></td>
    </tr>
  </tbody>
</table>

<p>Work in progress</p>

<h1 id="26-leaderboards-page">2.6 Leaderboards Page</h1>

<p>In the Leaderboards Page you will be able to see your current position in the leaderboards as well as others.</p>

<ul>
  <li>View your rank</li>
  <li>View other players rank</li>
</ul>

<p><img src="/assets/img/polyglot-pirates-testing/leaderboards.png" alt="leaderboards" /></p>

<p>Work in progress</p>

<h1 id="27-ship-page">2.7 Ship Page</h1>

<p>The Ship Page is a sort of get ready page before the game actually starts.</p>

<video controls="" loop="" autoplay="" muted="" style="height: 100vh">
    <source type="video/webm" src="/assets/img/polyglot-pirates-testing/ship.webm" />
</video>

<p>Work in progress</p>]]></content><author><name>dragos</name></author><category term="game" /><summary type="html"><![CDATA[]]></summary></entry></feed>