<?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: Peter Vanderhook</title>
    <description>The latest articles on DEV Community by Peter Vanderhook (@petervanderhook).</description>
    <link>https://dev.to/petervanderhook</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%2F2910988%2Fd3fef59c-68b3-4e00-9e6b-573ddf6b34d5.png</url>
      <title>DEV Community: Peter Vanderhook</title>
      <link>https://dev.to/petervanderhook</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/petervanderhook"/>
    <language>en</language>
    <item>
      <title>Godot 3D MMO: Server and Network Infrastructure, Authentication and Security, Gameserver Sharding</title>
      <dc:creator>Peter Vanderhook</dc:creator>
      <pubDate>Wed, 11 Jun 2025 05:48:14 +0000</pubDate>
      <link>https://dev.to/petervanderhook/godot-3d-mmo-server-and-network-infrastructure-authentication-and-security-gameserver-sharding-48kh</link>
      <guid>https://dev.to/petervanderhook/godot-3d-mmo-server-and-network-infrastructure-authentication-and-security-gameserver-sharding-48kh</guid>
      <description>&lt;h2&gt;
  
  
  Starting from scratch
&lt;/h2&gt;

&lt;p&gt;This is a follow up to my previous post &lt;a href="https://dev.to/petervanderhook/diving-head-first-into-creating-a-multiplayer-game-my-experience-4c61"&gt;here&lt;/a&gt; where I created a 3D multiplayer game in Godot. I ran into some issues with server authority with physics and decided I would need to restart the project from scratch. Unlike before, this is more of a "Where we're at" post as opposed to a "how we got here".&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Tech Stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;SQL, MongoDB, NodeJS, Godot, Javacript, Python, AWS&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Networking Setup
&lt;/h2&gt;

&lt;p&gt;This server was modeled for use with AWS. I am creating instances within VPCs for each server component. Currently, each server is set up manually, however I do have plans to use terraform and ansible to automate scaling of this entirely. (I just feel like that is not a necessity until I at least start playtesting)&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%2Fomhcctbqtuya6zurcrwq.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%2Fomhcctbqtuya6zurcrwq.png" alt="Network Infrastructure Diagram" width="800" height="383"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://i.gyazo.com/38f4bfecaaa493bf74bdb28610cf5347.png" rel="noopener noreferrer"&gt;(better quality)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We essentially have 3 main components. The auth server (authenticates users against the users database), server collections (groups of game-servers that represent a single world with IDs such as us-west-1, etc), and the server manager (managers authentication servers and server collections). &lt;/p&gt;

&lt;h2&gt;
  
  
  Server Manager
&lt;/h2&gt;

&lt;p&gt;The server manager allows connections from only whitelisted IPs (Auth servers and collections) on a websocket server (HTTPS), all other inbound traffic is blocked at the network level via security groups. The server essentially manages the state of all gameserver collections and auth servers, and acts as a medium between the two. This server has direct access to a non-relational database for players and manages communication between the gameservers and the DB. Nginx handles the routing for inbound TLS connections and routes them internally to the websocket server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auth Server
&lt;/h2&gt;

&lt;p&gt;The auth server is serving a basic website and connects to the server manager websocket. There are a few endpoints, /register, and /login, that will perform inserts/checks against the database to create users and log them in. Users will send a few extra fields when POSTing to the /login endpoint from the game client to allow for secure sign-in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Collections
&lt;/h2&gt;

&lt;p&gt;Collections (As I've called them) represent a group of game servers. Each game server zone is instanced onto a separate instance within a network, each listening on a different port. Nginx will handle inbound connections and route them to the appropriate gameserver. Since a server world may have multiple zones, I've broken each zone down into its own server and a collection is the entire group of servers. Server manager will store each gameserver in memory and sort them into groups based on their collection ID. &lt;/p&gt;

&lt;h2&gt;
  
  
  Authenticated/Secure Login process
&lt;/h2&gt;

&lt;p&gt;When a client logs in, communication can not be sent directly to the gameserver as this connection is not encrypted (to allow for faster processing of gameserver packets). Thus, we must send an encrypted connection request to the auth server, and have that route itself to the gameserver securely. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The process for a client logging in is essentially this.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When a client starts the executable, they are greeted with a server selection (Client performs a GET request to a page listening on AUTH server.&lt;/li&gt;
&lt;li&gt;This page returns a list of all collections, the server connection information, player count, etc. &lt;/li&gt;
&lt;li&gt;The client will load this into a selectable list which, when a server is chosen and loaded, the client will connect to that game server. &lt;/li&gt;
&lt;li&gt;Once connected, the game server assigns the client a Peer ID and tell the player to load the login screen.&lt;/li&gt;
&lt;li&gt;When the player logs in, they send an encrypted /login post to the auth server, with the server collection they are connected to along with their Peer ID assigned to them by the game server.&lt;/li&gt;
&lt;li&gt;The auth server, on successful login, will send a request to the server manager essentially saying: "Player X wants to connect to Collection X"&lt;/li&gt;
&lt;li&gt;The server manager, on receiving this from the auth server, will check if the user has a player character. &lt;/li&gt;
&lt;li&gt;If they don't, one is created for them and then it checks if they exist again. &lt;/li&gt;
&lt;li&gt;If they do, server manager tells 'collection X' to load player of peer_id X into the location pulled from the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the initial log in, credentials are cached on the client so that the server can call remote connections to other gameservers automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sharding
&lt;/h2&gt;

&lt;p&gt;Initially one game server would handle every zone in the game, but this was going to be intensive as all physics, items, players, etc would need to be simulated on the host at the same time to ensure everything is working as the server intends. To ease the load on the game servers, I've broken down each zone into its own server, and then added functionality to allow gameservers to send a player from gameserver A to gameserver B in the collection without them noticing. &lt;/p&gt;

&lt;p&gt;When a client switches to a new gameserver within the collection (like switching to another zone or area managed by a different server), the process above is repeated after their location and position are updated in the database so that they are routed to the appropriate server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Video demonstration&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Current plans
&lt;/h2&gt;

&lt;p&gt;The project got pretty hectic setting up sharding, and I have functions in some spots that should be in others, etc. So my current plan is to clean up the game client code and add some better comments to make everything easier to understand.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Diving Head-First Into Creating a Multiplayer Game — My Experience</title>
      <dc:creator>Peter Vanderhook</dc:creator>
      <pubDate>Wed, 05 Mar 2025 00:39:59 +0000</pubDate>
      <link>https://dev.to/petervanderhook/diving-head-first-into-creating-a-multiplayer-game-my-experience-4c61</link>
      <guid>https://dev.to/petervanderhook/diving-head-first-into-creating-a-multiplayer-game-my-experience-4c61</guid>
      <description>&lt;h2&gt;
  
  
  How I got started in game development
&lt;/h2&gt;

&lt;p&gt;I've been passionate about making games since I was a kid. My first experience with game development came through the &lt;em&gt;Age of Mythology&lt;/em&gt; scenario editor, where I learned the basics of triggers, loops, and conditional statements. This introduced me to the fundamentals of programming and game development.&lt;/p&gt;

&lt;p&gt;After experimenting with game modifications during my childhood, I created my first original game in 2014—a text-based RPG called Dragonslayer, using Python 2. Since then, I've worked on a variety of games and had the opportunity to participate in two community game jams.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting this project.
&lt;/h2&gt;

&lt;p&gt;The concept of MMOs has always fascinated me. The freedom to choose your own experience in a world populated by thousands of players excites me. I love the social aspect, and the idea that, with an internet connection, you can always pick up right where you left off, with others to keep you company.&lt;/p&gt;

&lt;p&gt;Up until now I had been working with databases and CRUD applications, and I'd also created single-player games. I knew I wanted to combine these experiences into a larger project.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Initial Stages
&lt;/h2&gt;

&lt;p&gt;I began working in Unity, setting up a basic third-person character model and controller.&lt;br&gt;
&lt;br&gt;
After that, I set the camera to an isometric point of view and added a few objects to the world.&lt;br&gt;
&lt;br&gt;
To keep the player’s experience smooth, I created an invisible 3D point that followed and linearly interpolated its position based on the player’s distance. This allowed for free movement without affecting the point of view when near the center of the screen.&lt;/p&gt;

&lt;p&gt;The goal here was to simulate how the game would look before adding any real content. I wanted to explore everything and get a feel for the project. I’ve always liked the pixel/low poly art style (think Runescape or Aberoth), so I planned to recreate that look. However, drawing out sprites seemed like too much work, so I initially experimented with scaling the resolution. Unfortunately, there were some drawbacks—namely, the interface would also scale down, making the menu unreadable. &lt;br&gt;
&lt;br&gt;
Eventually, I found a solution. I would take the image from the character’s camera after it was drawn and "paste" it onto a 3D plane mesh, with the main camera pointed at it. This allowed me to scale the image down before drawing the interface, keeping the UI at a high resolution.&lt;br&gt;
&lt;br&gt;
It was much better, but there was one major issue: there was no depth. The raycasting used to interpret the mouse position relative to the world didn’t work when the image was displayed on a plane mesh.&lt;br&gt;
&lt;br&gt;
This took me forever to troubleshoot. Once I figured out the issue, I decided to abandon this approach.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Solved by Shader
&lt;/h2&gt;

&lt;p&gt;After watching a few basic tutorials, I managed to put together a shader that solved the raycasting issue.&lt;br&gt;
&lt;br&gt;
Although this worked, I wasn’t entirely happy with the way it looked. Since Unity’s render pipeline provided more flexibility, I decided to experiment further. I added a toon shader effect to simulate edge detection.&lt;br&gt;
&lt;br&gt;
It looked nice, but a bit too neon for my taste, so I adjusted the colors.&lt;br&gt;
&lt;br&gt;
At this point, I was much happier with the results. However, I began feeling a bit uneasy with all the policy changes around Unity, which led me to explore other options.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Switching to Godot
&lt;/h2&gt;

&lt;p&gt;&lt;br&gt;
My friend approached me a couple months after I finished the initial prototype with a project of his own and asked if I’d like to collaborate. He had already set up a basic first-person controller, so I added some shaders to his project. Like before, we were able to scale the resolution without affecting the user interface.&lt;br&gt;
&lt;br&gt;
The graphics still felt a bit bland, so I spent some time adding an internal calendar, day/night cycle with color blending, and implemented edge detection.&lt;br&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  4. First Obstacle in Godot
&lt;/h2&gt;

&lt;p&gt;Things were starting to feel much better, but I encountered an issue when implementing items in the overworld. Specifically, any 3D model with an image texture wouldn’t render properly after a certain distance from the camera. The items would be invisible but still "picked up" and populate the inventory.&lt;/p&gt;

&lt;p&gt;To get around this, I tried replacing the image-textured 3D models with spinning plane meshes, textured in Blender. While this worked somewhat, it still didn’t feel quite right.&lt;br&gt;
&lt;br&gt;
After speaking with others, the consensus was that this issue stemmed from an underlying problem with Godot’s renderer. Since I was using Godot 4.0 at the time (now on 4.3+), it was likely patched later, but I had to find another solution.&lt;br&gt;
 &lt;br&gt;
I ultimately decided to go back to using the 3D model approach. While more resource-intensive, it looked much nicer. I believe it will work great as long as the models remain low-poly and proper garbage collection is implemented to prevent item stagnation.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Adding Animations
&lt;/h2&gt;

&lt;p&gt;While my friend wrapped up the inventory system and equipment handler, I focused on NPC animations. I added a rabbit model with functions to manipulate its animations and behaviors, to be triggered by the server.&lt;br&gt;
&lt;br&gt;
I created three base animations and used time scales and animation blending to create smooth transitions, allowing more complex movements from simpler animations.&lt;br&gt;
&lt;br&gt;
For testing, I set the rabbit to change its animation based on the distance from the player. The animations switched smoothly, showing that the linear interpolation on the transitions was working correctly.&lt;br&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Particles, Weapons, Destructible Terrain
&lt;/h2&gt;

&lt;p&gt;By this point, both my friend and I felt more confident about the current state of the inventory/weapon system. &lt;br&gt;
&lt;/p&gt;

&lt;p&gt;We also added a staff that could cast spells, and I enhanced the projectiles with particle effects. For example, arrows emit a trail-like particle, while fireballs emit flames, smoke, and an explosion of particles on impact.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Add in some mana, potions, and destructible houses and you can start to see it all come together.&lt;br&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Implementing the Server
&lt;/h2&gt;

&lt;p&gt;Ideally, multiplayer games are designed with the intention of being multiplayer from the beginning. Since this was a prototype for educational purposes, we took the harder route.&lt;/p&gt;

&lt;p&gt;To connect users to the server, my friend created a simple UI, and I added functions to capture the server info from the input box and establish the connection.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;I set up a basic WebSocket server in Node.js and implemented essential functions like connecting the user, handling disconnections, and maintaining a heartbeat to check for dropped connections.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;Once the handshake was established, I focused on persistence. When a player disconnects, the server saves their properties to the database. When they reconnect, their unique hardware ID is used to retrieve their data and update it.&lt;br&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%2F0kn59pc8djo9f76l9t23.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%2F0kn59pc8djo9f76l9t23.png" alt="User Connection Code" width="800" height="646"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I created server modules and stored data in a simple SQLite database, with constant items and spells tables.&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%2F9iupqbrgun789hb2tazs.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%2F9iupqbrgun789hb2tazs.png" alt="Item Database" width="800" height="555"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The challenge was that database calls were synchronous, meaning the code would attempt to access values before the query had finished. To solve this, I implemented promises to ensure the server would wait for the response before continuing.&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%2Fp7oaegxi0d4xndurfue5.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%2Fp7oaegxi0d4xndurfue5.png" alt="Promise Code for Database" width="438" height="609"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Security
&lt;/h2&gt;

&lt;p&gt;For multiplayer games, server authority is crucial. Without it, players can manipulate data, such as movement speed. For example, a client could send a packet saying &lt;code&gt;{'player':'User1', 'speed': 10}&lt;/code&gt;, but what if the actual maximum speed should be 7? Server authority ensures the data processed by the server is valid.&lt;/p&gt;

&lt;p&gt;Here’s how the server handles player positions.&lt;br&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%2Frcn4upice7srukwh9a6u.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%2Frcn4upice7srukwh9a6u.png" alt="Server Authority" width="419" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We pass three vectors to represent the user and their position: Direction (where they are looking), Rotation (the user's orientation in radians), and Position (where they are relative to the center of the world). Knowing these values, along with the user's keyboard inputs, allows the server to simulate the movement of the players and send the data to all other players, linking those movement actions to a unique player ID which is displayed on each connected user's screen.&lt;/p&gt;

&lt;p&gt;We can see after implementing that it's working, as the players are drawing the correct position, orientation, and direction of the player object.&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;There was a slight issue with the model's orientation in this clip— we were rotating the parent object instead of just the "head." Additionally, old player models weren't being discarded, creating a memory leak.&lt;br&gt;
&lt;br&gt;
A few minor changes to the code later, and it's working great. We’re now sending inputs to the server with a direction, rotation, and position. Additionally, we're checking to make sure the distance the player has traveled since the last packet isn't too far. If it is, the server reverts to the original position to prevent speed hacks.&lt;/p&gt;

&lt;p&gt;In that setup, I was sending roughly eight transform packets per second. Ideally, you would implement linear interpolation to "guesstimate" the player's direction and movements, allowing for less traffic over the channel and a smoother, faster experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Next Steps and Major Problems
&lt;/h2&gt;

&lt;p&gt;While this all sounds great, there is an unforgivable flaw in this design that will ultimately require me to rebuild everything from scratch. When a player tells the server their new position, and the server checks the distance traveled to ensure they aren’t moving too fast, there’s no system in place to check for collisions.&lt;/p&gt;

&lt;p&gt;Here is a rough diagram I’ve made to demonstrate:&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%2F1cw4va1ldn1qfrtzr8ba.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%2F1cw4va1ldn1qfrtzr8ba.png" alt="FIRST MOVE IS LEGAL AND SERVER THINKS ITS LEGAL" width="800" height="491"&gt;&lt;/a&gt;&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%2Fiuhi9xu37p9tbg8hngvw.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%2Fiuhi9xu37p9tbg8hngvw.png" alt="FIRST MOVE IS ILLEGAL AND SERVER THINKS ITS LEGAL" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As shown, when the player moves through the world, the server only checks for the distance traveled but doesn’t validate whether the player has collided with something like a wall or obstacle. The current system assumes that any move within the distance limit is valid, but this could allow players to bypass obstacles.&lt;/p&gt;

&lt;p&gt;To fix this, I need to simulate all of the physics on the server and ensure that the server maintains authority over what collisions occur vs. what is simulated. The best solution seems to be adding server-side code into the game and designating one 'client' as the host. This host will simulate everything just like the engine would, but it can run in a graphic-less state to preserve resources.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This is as far as I’ve gotten in the year or so I’ve been working on this project. It’s my first-ever non-LAN multiplayer project, and I’m sure I will make more mistakes along the way. But this experience has taught me so much, and I feel much more confident and prepared to tackle round two of this project.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
