Dean's Blog Development blog of Dean Harding. en-us Sat, 05 Mar 2016 21:13:13 GMT March 2016 gameplay update Sat, 05 Mar 2016 21:13:13 GMT <p>A lot has happened in the last month. Here&#39;s the latest gameplay video:</p><p>&nbsp;</p><p style="text-align: center;"><iframe width="420" height="315" src="" frameborder="0" allowfullscreen=""></iframe></p><p>Here&#39;s a quick rundown of the changes:</p><ul><li>Particle effects for entities. You can see this in the blacksmith smoke and fire towards the end.</li><li>Clothes/armour (though only clothes implemented for now, armour is the same: just a different sub-model). This is actually somewhat tricky, the way it works is, we have a &#39;base&#39; mesh that contains a skeleton and the animations, then we have additional meshes for feet, hands, torso, legs and head that attach to that skeleton. So I can replace each of those components idependently of the others.</li><li>The inventory system actually lets you swap items out and it updates the preview at the same time.</li></ul><p>So that&#39;s it for now. This month I plan to work on dungeons and dungeon-generator.</p> Client/server architecture Sat, 05 Mar 2016 10:13:00 GMT <p>In this post, I&#39;m going to describe the client/server &quot;architecture&quot; of my as-yet unnamed RPG, such as it is. The basic premise of the game is that it&#39;s an open-world, browser-based role-playing game. My expectation is that there could be ~100 players online at a time, and the game should be able to reasonably handle that on a single server. Currently I have no plans for how to handle expanding the game past a single server, apart from some vague ideas about splitting the world geographically.</p><p><span style="font-size: 12pt;">As mentioned in my last post, the game runs in a browser, so the client is JavaScript. I&#39;ve chosen to use&nbsp;Go&nbsp;for the server, mainly because I wanted to try something a bit different, and I like Go&#39;s concurrency model. So why not?</span></p><h1><span style="font-size: 12pt;">Overview</span></h1><p><span style="font-size: 12pt;">With that said, here&#39;s a picture of how the server looks today:</span></p><p style="text-align: center;"><img alt="" data-resp="eyJzdWNjZXNzIjp0cnVlLCJ1cmwiOiJodHRwOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS9yeC1LZjNJLTk4QWdjTnZJZWl1eFJYUkdnU0ZGRDloQm5lWndhc2lfMXZMNUktOXJ6bFN6UllLcmZTLWNrQnRDMzFMVEJ1d1paSkNSUXVlSm1kQ25MWWs9czEwMCIsImJsb2Jfa2V5IjoiQU1JZnY5NFVpNmh6UDZvc3RzcmNLNkhmWENYd3l6ZF9lcWh5MVFGbG9JRGhVcnBXM1BzVXBsa1RZNjBhNUlWRUdwNUxMT09CLTdmS2Q0bjhYWmVYUHROWmRnTkFueXhXZFF5UXdRNnh0eW9mdEJ2RGhBS0hSOUtFRFJSMjY5Sk5RbXVUQzFwR19OWlVPN3h4OTNWWVVud1ptYVVoUlZRNVNBIiwiaGVpZ2h0Ijo3MjAsIndpZHRoIjo0MTAsImZpbGVuYW1lIjoiQXJjaGl0ZWN0dXJlIGRpYWdyYW0ucG5nIiwic2l6ZSI6MTM4NDgxfQ==" src="" /></p><p>From top-to-bottom we have:</p><ol><li>The actual game clients, running JavaScript in a browser. These connect via a&nbsp;persistent WebSocket&nbsp;to the server.</li><li>In the server&#39;s main process, one &quot;player handler&quot; exists for each connected player. The player handler keeps a reference to the player&#39;s entity in the world.</li><li>The &quot;world&quot; is basically a collection of entities and some supporting data structures (e.g. pathing objects for constructing paths, etc) and a bunch of goroutines acting on those entities.</li><li>The &quot;long-term storage&quot; (or just &quot;Store&quot; as it&#39;s called in the code) is where we store all the things which need to persist between server restarts (things like player stats, inventory, quest status and so on)</li><li>The &quot;sectors&quot; collection is a bunch of static files which describe a segment of the world. These are 64x64 grids that contain information about the terrain, the entities that should be initially created on the terrain and so on.</li></ol><h1>Entities</h1><p><span style="font-size: 12pt;">Entities are simply a collection of &quot;components&quot;. A component provides the basic functionality of the entities, and can be combined to form any kind of complex entity we like. The components used on the server and client are not always the same (for example, the &quot;Model&quot; component is ignored on the server, but it&#39;s obviously very important on the client!)</span></p><p><span style="font-size: 12pt;">Some of the components we have defined are described below.</span></p><ul><li><span style="font-size: 12pt;"><em>Position</em>&nbsp;- This contains the coordinates of the entity. It also handles things like &quot;follow terrain&quot; which causes an entity to &quot;stick&quot; to the terrain, or allows it to float above the terrain.</span></li><li><span style="font-size: 12pt;"><em>Movement</em>&nbsp;- For entities which can move, this handles finding paths (on the server) and following paths (on the client).</span></li><li><span style="font-size: 12pt;"><em>Model</em>&nbsp;- On the client, this is used to control the model that the entity is displayed as. Things like animations are controlled by this component as well.</span></li><li><span style="font-size: 12pt;"><em>Mob</em>&nbsp;- This component controls the &quot;AI&quot; of mobs. When not attacking a player, a mob will wander around at random. When attacked by a player, the mob will attempt to fight back.</span></li><li><span style="font-size: 12pt;"><em>Stats</em>&nbsp;- This component manages the entity&#39;s stats (health, strength, etc).</span></li><li><span style="font-size: 12pt;"><em>Spawner</em>&nbsp;- This component is used by entities on the server to spawn other entities. It&#39;s usually invisible and lets you control things like &quot;spawn X kind of entity, making sure there&#39;s never more than N entities active at once).</span></li></ul><h1><span style="font-size: 12pt;">Sectors</span></h1><p><span style="font-size: 12pt;">The world is divided up in 64x64 grids, called &quot;sectors&quot;. The sectors are all adjacent to each other, and the client will load up to four sectors to ensure that the world appears seemless as you move around. On the server, sectors aren&#39;t really used at runtime, except that the data within them is loaded on demand the first time a client enters that sector.</span></p><p><span style="font-size: 12pt;">For example, the first time a player enters a particuar sector, the server will load all the data for that server in to memory (for example, there is an entities.json file which contains definitions for all of the entities which exist in that sector (for example, this will describe environmental objects -- trees, rocks etc, as well as things like spawners and NPCs to populate the world with life.</span></p><h1><span style="font-size: 12pt;">Long term storage</span></h1><p><span style="font-size: 12pt;">Long-term storage (or just the &quot;Store&quot;) is used to store all the data which needs to persist over server restarts. Things like player stats, inventory, quest status and so on. Every time one of these things is modified in the world, the store is automatically and immediately updated as well.</span></p><p><span style="font-size: 12pt;">It&#39;s important to note that entities in the game are not&nbsp;nessecarily&nbsp;stored in the persistent store. We generally assume that if the server restarts, then when the sector is reloaded, all the entities we care about will be re-created from the sector data.</span></p><h1><span style="font-size: 12pt;">Client/server protocol</span></h1><p><span style="font-size: 12pt;">As mentioned above, the client maintains a persistent WebSocket connection to the server. If the WebSocket connection drops out, the client pops up a &quot;waiting to reconnect&quot; dialog and tries to re-connect.</span></p><p style="text-align: center;"><a title="" class="lightbox" href=""><img alt="" data-resp="eyJzdWNjZXNzIjp0cnVlLCJ1cmwiOiJodHRwOi8vbGgzLmdvb2dsZXVzZXJjb250ZW50LmNvbS8zUkFUYjhwQm1udEVhNzVBYktXVTZzLXdXM3F6d0FsQThVdnE2WElaYVhBUXRNVGVqQk14dllzc2haNWV0eU1KVzRnOXVfYXpJbXNYeERsZllwbUY9czEwMCIsImJsb2Jfa2V5IjoiQU1JZnY5NlVpUlY5U3dBcEtKQkZycGdRT2pBXzJkbTFVU0FaZkFlTVhjdUhEeHRQQ2dPVlNlUFNpWFNJbmRBLWUwekg3SmR6aXBqbnhVQ09td0EtTmJEbldQUk9sSVRfd21tYTRIaEprS3dXWjhHUWJNLUNuQmVYLUs3RjdNRl82YWdwaXVZaUxtS0hneU9haWVBdERTZFI5YTlfZWxic2hRIiwiaGVpZ2h0Ijo0MTAsIndpZHRoIjoxMjAwLCJmaWxlbmFtZSI6IlNjcmVlbiBTaG90IDIwMTYtMDEtMTAgYXQgMy4zMS4xNyBQTS5wbmciLCJzaXplIjozMTMzODR9" src="" /></a></p><p>The above shows Chrome&#39;s debug tool showing the frames sent over the WebServer to the server (and the data received back from it). Data is encoded using JSON (for now, I might switch to a binary format if size/bandwidth becomes a problem).</p><p><span style="font-size: 12pt;">Basically, on the server there is a player handler which handles decoding the packets from the client. It maintains a reference to the player&#39;s entity in the &quot;world&quot;, and whenever a packet instructs it to do something, the entity within the world is notified. For example, when the player clicks on another entity, the player handler will issue an &quot;activate&quot; event on the entity you clicked on. Depending on whether you clicked an NPC or a mob, the NPC might send a &quot;show quest page&quot; packet back to the client. Or if it&#39;s a mob, an &quot;entity attacked&quot; packet might be sent to all players within visual range.</span></p><h1><span style="font-size: 12pt;">Conclusion</span></h1><p><span style="font-size: 12pt;">That&#39;s a basic overview of the architecture of the game, such as it is today.</span></p><p><span style="font-size: 12pt;">Obviously this is all quite up in the air, and I haven&#39;t really arrived here through considered, careful thought, but more through an organic process of evolution.</span></p> February gameplay update Wed, 10 Feb 2016 10:00:00 GMT <p>Here&#39;s the latest update of where I&#39;m at with my still as-yet-unnamed RPG. First, a video:</p><p style="text-align: center;"><iframe width="560" height="315" src="" frameborder="0" allowfullscreen=""></iframe></p><p>As you can see, there&#39;s a bunch of new features. In no particular order:</p><ul><li>Chat works and looks much nicer. You can actually see who&#39;s chatting with you, for example.</li><li>Quest management: when you start a quest, an entry gets added to the &quot;Quests&quot; menu and you can see what tasks you need to do.</li><li>Inventory management: you can open up your inventory and see what you&#39;ve got equipped.</li></ul> The unnamed RPG Wed, 13 Jan 2016 10:46:00 GMT <p>This post provides a bit of an overview of my plans for my as-yet unnamed RPG. One of the biggest lessons I learned with my previous game (<a href="">which I talked a bit about here</a>) is that players <em>want to</em> interact. The in-game chat was pretty much an afterthought in my previous game (in fact, it was the very last feature I added before going to &quot;alpha&quot;). Most of the work I did post-release was community-based features -- an alliance system, one-on-one and group private chats, and so on.</p><p>So I wanted to make sure my new game supported a vibrant community right from the beginning. Also, I was a little tired of the limitations of working with a phone, so I wanted something that ran on a PC. But more than that, I wanted something that was cross-platform (I personally use a Mac laptop and a Linux workstation, so it needed to run on those platforms as well as Windows, of course). Because I&#39;m a masochist, I decided make the game browser-based.</p><p>The first iteration I was using a &lt;canvas&gt; directly and drawing 2D sprites. It worked pretty well, but performance was generally pretty horrible -- and that was with practically nothing on the screen! I actually got pretty far with that 2D engine, including multiple players, chat, animations and so on. Here&#39;s a video:</p><p style="text-align: center;"><iframe width="600" height="370" src="" frameborder="0" allowfullscreen=""></iframe></p><p>That video didn&#39;t have pathfinding, but that was also added before I decided that the performance just wasn&#39;t there. My plan was going to be to use webgl to speed up the 2D engine, but as I was coding, this happened</p><p style="text-align: center;"><a href=""><img width="550" height="250" src="" /></a></p><p>So I made it fully 3D with three.js as the backend renderer. In fact it&#39;s actually not that hard to get webgl up and running -- the hardest part, at least for me, was content. The initial &quot;rewrite&quot; was still talking to the same backend server, so it still supported all those wonderful multiplayer features, but I had an MD2 model (from Quake) as my main character, and some procedurally generated tree I found somewhere as decoration.</p><p style="text-align: center;"><iframe width="600" height="450" src="" frameborder="0" allowfullscreen=""></iframe></p><p>The most interesting thing about the rewrite is the terrain. I found <a href="">this cool article on Gamasutra</a> and implemented it as a GLSL shader in three.js.</p><p>Then I taught myself how to use blender to create some basic 3D objects and animations. All the content in the <a href="">current gameplay video</a> was made by me in blender. Yay!</p><p>So that&#39;s where we are today. I&#39;ve have a few ideas for the basic premise behind the game and also some information on how the game has been &quot;architectured&quot; (I put it in quotes because it&#39;s much more organically developed than architectured...)</p> What's the deal with USB headphones? Sun, 22 Feb 2015 12:21:59 GMT <p>Why do these things exist? A while back, I bought a pair of Logitech&nbsp;H390 headphones. This is what they look like:</p><p style="text-align: center;"><img alt="" data-resp="eyJzdWNjZXNzIjp0cnVlLCJ1cmwiOiJodHRwOi8vbGg0LmdncGh0LmNvbS9uSEVFNklzRDFBUUlRVFBVTmM4ZGVVMkZZZUNrUS1fSEhXWDlyWjY0ZDVVSVVHT1VZT280bzM0VTcwTEEwTDRtVHVrM0Y0VzhZaHowMDF4eVljVTE9czEwMCIsImJsb2Jfa2V5IjoiQU1JZnY5NlkxWG9DSnFEajVsTnlqSUZlNGc1NUJVY0pqNElNNnlEWHdfeE1nQUctQkZnYXhtNG1HWk5lX3o4RlBfRU5fRWEwVmU0ejFGVWNFZDBGNHk4cVVaTFJUbWJwbXFVRy1ES1N5aUE0OWhYUWNSSWctNWY5MXdzb0hvX19uRUZWbHVhVkN3Z29YYksyTlBlamRzZ19TSXlpR0p0akZnIiwiaGVpZ2h0IjozMjAsIndpZHRoIjo0MjksImZpbGVuYW1lIjoibWVkaXVtLnBuZyIsInNpemUiOjI2MjU3MX0=" src="" /></p><p>When I bought them, I didn&#39;t really spend a whole lot of time looking at the box, but the fact that the interface is USB is shown in that little info panel on the bottom-left. In retrospect, I probably should have spend more time reading that box because it turns out, USB is a horrible, weird interface for headphones and I can&#39;t for the life of me understand why they exist.</p><h2>Cons of USB headphones</h2><p>These are just the ones I ran into while using my headphones over the last year or so:</p><ul><li>They only work in a couple of places. PC, sure. Maybe I could plug them into my Android phone with a USB-A to micro-USB adapter, but I never bothered to buy one in case it didn&#39;t work. A Ninentdo DS or practically anything else that&#39;s not a PC? Forget it!</li><li>Even when it works, because the headset shows up as a separate device, you can get yourself into weird situations where audio would continue routing to your main speakers and not the headphones. Both Windows (as of version 7, I haven&#39;t tried newer versions) and Linux have fairly clunky interfaces for choosing which output device to use. In most cases, with regular headphones, once you plug in all audio is automatically routed to the headphones.</li><li>Because the DAC happens inside the headphones, you can be pretty sure the quality of that hardward is nowhere near the quality of the hardware in your desktop computer.</li></ul><h2>Pros of USB headphones</h2><p>&macr;\_(ツ)_/&macr;</p><p>Oh wait, I can think of one &quot;pro&quot;. The <a href="">Creative Tactic3D Rage</a> headphones has illuminated ear cups. For some reason.</p><p style="text-align: center;"><img alt="" data-resp="eyJzdWNjZXNzIjp0cnVlLCJ1cmwiOiJodHRwOi8vbGg0LmdncGh0LmNvbS9samZtMjZBeldrN2tjcWpkOE1WeXJoY0poRnF1dFY4cTQ0dTAzbzFNamU4OFNzWkpDQ2drbmNwS0l4UXY2NHNEeDNtTVp5YlFPcFpMa0dBdTdZYzlJX3c9czEwMCIsImJsb2Jfa2V5IjoiQU1JZnY5NTFBM0xWWWNyNERITVpWekFzeEJKcEkxeVQ2WGZWMTlCZFc3SEtrM2hlYmpGanZ3MlBMVDgyZ2dnSHpGQzhKQnZJVl9GckJ6QTlrWGRvOV9sZlg5d0Z0OVZnSXpwN09HUjlDM19FV2tNSXBTa3E3U0NDUllLX3ByTnJMTTBaVDBYX2x1WUpoemRCdm0zQXhnSnpZZktJdXg2cWhBIiwiaGVpZ2h0IjoxODgsIndpZHRoIjo5NDQsImZpbGVuYW1lIjoiU2NyZWVuc2hvdCBmcm9tIDIwMTUtMDItMjIgMjM6MTk6MjYucG5nIiwic2l6ZSI6MTMwODM2fQ==" src="" /></p>