<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Konstantinos Mazgaltzidis</title>
    <description>The latest articles on DEV Community by Konstantinos Mazgaltzidis (@konmaz).</description>
    <link>https://dev.to/konmaz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1037398%2Ff17111a7-778f-45e6-828a-c590840c8fbb.png</url>
      <title>DEV Community: Konstantinos Mazgaltzidis</title>
      <link>https://dev.to/konmaz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/konmaz"/>
    <language>en</language>
    <item>
      <title>GUI in GitHub Codespaces</title>
      <dc:creator>Konstantinos Mazgaltzidis</dc:creator>
      <pubDate>Mon, 25 Mar 2024 12:21:13 +0000</pubDate>
      <link>https://dev.to/konmaz/gui-in-github-codespaces-jl0</link>
      <guid>https://dev.to/konmaz/gui-in-github-codespaces-jl0</guid>
      <description>&lt;h1&gt;
  
  
  GitHub Codespaces with VNC Support
&lt;/h1&gt;

&lt;p&gt;GitHub Codespaces offers developers an instant, cloud-based development environment with a plethora of tools and utilities, all accessible directly from your browser. While it's perfect for many development tasks such as command line tools, Docker, or NodeJS, certain tasks, like those involving OpenGL rendering or Java Swing necessitate a graphical interface, which Codespaces lacks. However, with the help of a tool called &lt;strong&gt;Light-weight Desktop (desktop-lite)&lt;/strong&gt;, we can overcome this limitation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up VNC Support with Desktop-lite
&lt;/h2&gt;

&lt;p&gt;To enable VNC support in Codespaces, follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to the option “Configure dev container” from the Codespaces dropdown menu.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lwthd17q383p9jx3zxd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lwthd17q383p9jx3zxd.png" alt="Image description" width="593" height="358"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Replace the existing JSON configuration with the following:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcr.microsoft.com/devcontainers/universal:2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"features"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ghcr.io/devcontainers/features/desktop-lite:1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"forwardPorts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;6080&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"portsAttributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"6080"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"desktop"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Commit the changes and open the Codespaces environment. If prompted to rebuild the container to apply the changes, proceed with the rebuild.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxyq14f376e17sp3ldx8p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxyq14f376e17sp3ldx8p.png" alt="Image description" width="459" height="114"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on the bottom icon labeled "Ports" and select the "Open in Browser" option.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjgu6hkta8u9bhgwkvce2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjgu6hkta8u9bhgwkvce2.png" alt="Image description" width="564" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the default password &lt;code&gt;vscode&lt;/code&gt; to connect.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fslooqyqt4nxlxc8vra4i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fslooqyqt4nxlxc8vra4i.png" alt="Image description" width="498" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In the Settings, select the &lt;code&gt;Scaling Mode&lt;/code&gt; to &lt;code&gt;Remote Resizing&lt;/code&gt; for a better user experience.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mqk48siyv5cndcpbhgd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2mqk48siyv5cndcpbhgd.png" alt="Image description" width="359" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can now navigate to your project path as seen in VS Code by using the command
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /workspaces/&amp;lt;repo_name&amp;gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Any changes made here will reflect in the VS Code environment seamlessly.&lt;/p&gt;

</description>
      <category>githubcodespace</category>
      <category>github</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I Created an Online Multiplayer Game Using Colyseus</title>
      <dc:creator>Konstantinos Mazgaltzidis</dc:creator>
      <pubDate>Sun, 24 Mar 2024 09:37:44 +0000</pubDate>
      <link>https://dev.to/konmaz/how-i-created-an-online-multiplayer-game-using-colyseus-npf</link>
      <guid>https://dev.to/konmaz/how-i-created-an-online-multiplayer-game-using-colyseus-npf</guid>
      <description>&lt;h1&gt;
  
  
  &lt;strong&gt;How I Created an Online Multiplayer Game Using Colyseus&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;You can find the source code for the &lt;a href="https://github.com/konmaz/colyseus_server/" rel="noopener noreferrer"&gt;Colyseus Server&lt;/a&gt; and the &lt;a href="https://github.com/konmaz/quiz_vue" rel="noopener noreferrer"&gt;Vue.js&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;Let's delve into the world of Trivia Games, where players engage in competitive quizzes, testing their knowledge. This article aims to explore the process of creating a browser-based multiplayer trivia game using Colyseus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Designing the Game Logic
&lt;/h3&gt;

&lt;p&gt;Before diving into the code, I began by sketching flowcharts to visualize the game's logic, such as player room allocation and question distribution. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Picture here&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Concept of (Game) Rooms
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmir-s3-cdn-cf.behance.net%2Fproject_modules%2Ffs%2Fdacb2964200385.5acacbccc27d9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmir-s3-cdn-cf.behance.net%2Fproject_modules%2Ffs%2Fdacb2964200385.5acacbccc27d9.jpg" alt="[Ryan Doede](https://www.behance.net/gallery/64200385/Isometric-Rooms-(CGI)/modules/376280015)" width="800" height="773"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.behance.net/gallery/64200385/Isometric-Rooms-(CGI)/modules/376280015" rel="noopener noreferrer"&gt;Ryan Doede&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The concept of rooms is not a new one, and it's not limited to multiplayer games. Many online experiences, such as video conference meetings or group chats, follow a room-based pattern. In the context of our game, rooms provide a virtual space for players to join and play with their friends.&lt;/p&gt;

&lt;p&gt;Rooms is a paradigm that can helps isolate each game session into its own room.&lt;/p&gt;

&lt;p&gt;Initially, a player creates a room and then invites the other players to join.&lt;/p&gt;

&lt;h3&gt;
  
  
  The First Prototype: Exploring Ably
&lt;/h3&gt;

&lt;p&gt;In the initial stages of development, I opted to use Ably as the communication platform. Ably offers a generous free plan (6 million messages per month) and has the ability to minimize latency by players joining from different parts of the world. In a traditional server-client model, players would connect to a common server, potentially leading to latency issues. However, Ably's infrastructure can help mitigate this problem.&lt;/p&gt;

&lt;p&gt;Ably follows the Pub-Sub (Publish-Subscribe) pattern, where messages are published to a channel and subscribers receive those messages. This pattern allows for real-time communication, making it suitable for multiplayer game development.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ghts4ur9tuyw28l8kjp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1ghts4ur9tuyw28l8kjp.png" alt="The Publish-Subscribe pattern" width="800" height="239"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Publish-Subscribe pattern&lt;/p&gt;

&lt;h3&gt;
  
  
  Designing the prototype
&lt;/h3&gt;

&lt;p&gt;But going with this route means that I will have to implement the creation of &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;rooms, managing authentication&lt;/li&gt;
&lt;li&gt;synchronization, state sharing (when an event is emitted the server process it and mutates the state, all clients need to get the latest state)&lt;/li&gt;
&lt;li&gt;client room creating/joining&lt;/li&gt;
&lt;li&gt;messaging format e.g. when a player respond to a question a message like the following needs to be sent &lt;code&gt;{"event_name":"player_answer_question", "data":{"player_response":1,"player_id":"foo"}}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;threading (!) to handle multiple rooms at the same time&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;But nevertheless, I continued anyway to explore this approach.&lt;/p&gt;

&lt;p&gt;I had also designed some flow charts of different scenarios (Room creation, game flow etc):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2obws14f0ny29v8zoz8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2obws14f0ny29v8zoz8.png" alt="Scenario Room Creation " width="800" height="1054"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Thankfully, Ably provides an elegant Dashboard, and a developer menu that I can see all the messages and join a channel (room).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftpd4csjy7mtmt56npzr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftpd4csjy7mtmt56npzr.png" alt="Ably Dashboard" width="800" height="317"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the prototype I used Python.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Prototype Code
&lt;/h3&gt;

&lt;p&gt;The code snippet you see here is a prototype for creating and joining rooms in a multiplayer game. It's a starting point for testing the concept, even though it might not be the most elegant solution.&lt;/p&gt;

&lt;p&gt;In this prototype, we create new rooms and spawn instances to manage them. When a message is received, we check if it's a 'wait' message, and if so, we introduce a delay. This represents a basic form of game logic where timing is essential.&lt;/p&gt;

&lt;p&gt;However, this approach requires writing a considerable amount of boilerplate code for even the simplest message exchange. Additionally, defining a common message format is necessary to ensure smooth communication between players and the server.&lt;/p&gt;

&lt;p&gt;This reminded me of a quote:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you aren't using a framework, you are building one&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So by choosing to use this approach, I would have to write code to handle the basic stuff.&lt;/p&gt;

&lt;p&gt;I have also have created some diagrams for this but I &lt;/p&gt;

&lt;h2&gt;
  
  
  Discarding the prototype
&lt;/h2&gt;

&lt;p&gt;I tried searching a different solution because using the former path was not viable, I was reinventing the wheel, and I was sure somebody had already solved the problem of a real-time multiplayer game server, so I searched for a solution.&lt;/p&gt;

&lt;p&gt;So I begin searching for a framework that could help me in my particular problem.&lt;/p&gt;

&lt;p&gt;My requirements for the framework that I was searching were:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Have a built-in way to handle room creations&lt;/li&gt;
&lt;li&gt;Support JavaScript (for the client side) by having a native client&lt;/li&gt;
&lt;li&gt;Have a solid documentation&lt;/li&gt;
&lt;li&gt;Easy event emitting and handling&lt;/li&gt;
&lt;li&gt;Be FOSS&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Colyseus
&lt;/h3&gt;

&lt;p&gt;The framework I stubble upon is called Colyseus, and I'd like to take a moment to elaborate on what makes Colyseus so good for my case. It is an open-source, actively developed, highly versatile framework designed to facilitate the development of multiplayer games and real-time applications.&lt;/p&gt;

&lt;p&gt;It provides a robust and efficient server-side infrastructure for handling the complexities of real-time interactions, making it an excellent choice for game developers, as well as anyone building applications that require synchronized and real-time experiences.&lt;/p&gt;

&lt;p&gt;In my personal experience, even with minimal prior exposure to Node.js, I found it surprisingly accessible to grasp the fundamentals of Colyseus. The clear documentation, along with the provided examples, enabled me to quickly understand the core concepts and principles of this framework. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmu2x7pt17a1a114we6l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvmu2x7pt17a1a114we6l.png" alt="Colyseus Playground" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It also provides a debugging interface a “&lt;a href="https://docs.colyseus.io/tools/playground/" rel="noopener noreferrer"&gt;Playground&lt;/a&gt;” that can show all the rooms that have been created, the state of each room, the players that have joined those room, and many more helpful features.&lt;/p&gt;

&lt;p&gt;Another alternative to Colyseus is &lt;a href="https://heroiclabs.com/nakama/" rel="noopener noreferrer"&gt;Namaka&lt;/a&gt; by Heroic Labs, but it requires a dedicated server (SSH-able) that can run a Docker container but Colyseus on the other hand only requires Node.js. &lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;Having a solid understanding from early of data flows, the game flow, and the architecture can speed up decision-making thought the development&lt;/p&gt;

&lt;p&gt;Colyseus follows the &lt;a href="https://www.gabrielgambetta.com/client-server-game-architecture.html" rel="noopener noreferrer"&gt;authoritative server&lt;/a&gt; paradigm, ensuring a singular truth for streamlined development and prevention of cheating. In essence, authoritative implies that the server maintains and disseminates the game state to clients. Clients merely need to present the state in a &lt;em&gt;visually appealing&lt;/em&gt; interface and emit events, for example when the user respond to a question an event is emitted and the server handles it.&lt;/p&gt;

&lt;h3&gt;
  
  
  State
&lt;/h3&gt;

&lt;p&gt;In our Quiz, the &lt;a href="https://docs.colyseus.io/state/" rel="noopener noreferrer"&gt;game state&lt;/a&gt; encompasses player scores, the ongoing question, the remaining time, the available 4 responses and each player's response details, such as when and what they answered.&lt;/p&gt;

&lt;p&gt;Colyseus provides an elegant method for defining this state, employing a schema that accommodates both straightforward data types like numbers and strings, as well as more intricate structures such as arrays and sets.&lt;/p&gt;

&lt;p&gt;I have defined the following schema for the Quiz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Import necessary classes from the Colyseus schema library&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ArraySchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;MapSchema&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@colyseus/schema&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Define the Player class representing the state of an individual player in the quiz&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Player&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Session ID uniquely identifying the player's session&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Player's username&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Number of lives the player has, initialized to 3 by default&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;lives&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Player's score, initialized to 0 by default&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Answer chosen by the player&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;player_answer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Time when the player provided an answer&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;player_answer_time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Counter for the number of consecutive correct answers&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;streak_correct&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Define the MyRoomState class representing the overall state of the quiz room&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyRoomState&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Schema&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Boolean indicating whether the game has started&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;gameHasStarted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Boolean indicating whether the game is over&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;boolean&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;gameOver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Category of the current question&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;questionCategory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Text of the current question&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;question&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Array of possible answers to the current question&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="nx"&gt;answers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ArraySchema&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Correct answer to the current question&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;correctAnswer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Map of players, where keys are player session IDs and values are instances of the Player class&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Player&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="nx"&gt;players&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;MapSchema&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Player&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Time remaining for the current question&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A visual representation of the player's and room state.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2abetxzedb5d3ruhcq6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2abetxzedb5d3ruhcq6.png" alt="Class Diagram of Player's &amp;amp; Room State" width="643" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The game flow
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn9bdqgusc41n9fwdut6f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn9bdqgusc41n9fwdut6f.png" alt="Game Flow Diagram" width="751" height="938"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Player Joining the Room&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;When a player joins the game room, their &lt;code&gt;sessionId&lt;/code&gt; and &lt;code&gt;username&lt;/code&gt; are assigned.&lt;/li&gt;
&lt;li&gt;This player object is then added to the &lt;code&gt;players&lt;/code&gt; map in the &lt;code&gt;MyRoomState&lt;/code&gt; class.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Starting the Game&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;Players can trigger the start of the game by sending a "start_game" message.&lt;/li&gt;
&lt;li&gt;Upon starting the game, the room broadcasts a "players_get_ready" message to all clients.&lt;/li&gt;
&lt;li&gt;The game checks if it has not already started (&lt;code&gt;gameHasStarted&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt;), and then locks the room to prevent new players from joining.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Game Loop&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;The game loop continues until the game ends (&lt;code&gt;gameOver&lt;/code&gt; becomes &lt;code&gt;true&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;During each iteration of the loop, the game follows these steps:

&lt;ul&gt;
&lt;li&gt;Generates a random question using the &lt;code&gt;QuestionsAPI&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Updates the game state with the new question, answers, and other relevant information.&lt;/li&gt;
&lt;li&gt;Starts a timer for players to answer the question.&lt;/li&gt;
&lt;li&gt;Listens for player answers and records them.&lt;/li&gt;
&lt;li&gt;If all players have answered or the timer runs out, the game proceeds to calculate scores.&lt;/li&gt;
&lt;li&gt;Checks if at least one player is still alive (has lives remaining). If not, sets &lt;code&gt;gameOver&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Calculating Scores&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;The game calculates scores based on player answers and time taken.&lt;/li&gt;
&lt;li&gt;If a player answers correctly, their score increases based on the time taken to answer.&lt;/li&gt;
&lt;li&gt;If a player answers incorrectly, they lose a life.&lt;/li&gt;
&lt;li&gt;Additionally, streaks of correct answers can grant extra lives.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ending the Game&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;The game ends if no player is alive or if a specific condition is met.&lt;/li&gt;
&lt;li&gt;Once the game is over, the room unlocks, allowing players to leave or join other rooms.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In summary, the game flow involves players joining the room, starting the game, iterating through the game loop to handle questions, answers, and scoring, and finally ending the game when certain conditions are met. Throughout this process, the game state (&lt;code&gt;MyRoomState&lt;/code&gt;) is continuously updated to reflect the current state of the game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;I opted for utilizing the free service provided by &lt;a href="http://render.com/" rel="noopener noreferrer"&gt;render.com&lt;/a&gt; to host both the Node.js Server (Colyseus) and the Vue.js web application (frontend).&lt;/p&gt;

&lt;p&gt;It's worth noting that the resources offered in the free tier of the Node.js may go into a dormant (spindown) state due to inactivity, resulting in potential delays of 50 seconds or more for requests.&lt;/p&gt;

&lt;p&gt;You can access the game &lt;a href="https://quizto.onrender.com/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, but please be aware that there might be a slight delay when creating a room. This delay is caused by the potential spin-down of the Node.js instance due to inactivity.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>node</category>
      <category>colyseus</category>
    </item>
  </channel>
</rss>
