<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Peter Serwylo</title>
 <link href="https://pserwylo.github.io//atom.xml" rel="self"/>
 <link href="https://pserwylo.github.io//"/>
 <updated>2024-03-15T00:15:20+00:00</updated>
 <id>https://pserwylo.github.io/</id>
 <author>
   <name>pserwylo</name>
   <email>peter@serwylo.com</email>
 </author>

 
 <entry>
   <title>Comprehensive tips for generating PDFs in the browser</title>
   <link href="https://pserwylo.github.io//2022/01/14/generate-pdfs-in-the-browser"/>
   <updated>2022-01-14T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2022/01/14/generate-pdfs-in-the-browser</id>
   <content type="html">&lt;p&gt;Recently, as an associate at &lt;a href=&quot;https://www.cognizant.com/au/en&quot;&gt;Cognizant Australia&lt;/a&gt;, I have found myself diving much deeper into the world of “Print to PDF” than I ever thought neccesary. This is a summary of my learnings in the hope it may help others.&lt;/p&gt;

&lt;h2 id=&quot;warning-work-in-progress&quot;&gt;WARNING: Work In Progress&lt;/h2&gt;

&lt;p&gt;This post is a work in progress. There are still sections left to complete, screenshots to add, proof reading to be done.&lt;/p&gt;

&lt;p&gt;However, it may be some time before I am able to complete it, and I hope someone can find the information here useful in the meantime.&lt;/p&gt;

&lt;h2 id=&quot;summary&quot;&gt;Summary&lt;/h2&gt;

&lt;p&gt;Your web app has users, sometimes they just want a PDF copy of what they are looking at on the screen.&lt;/p&gt;

&lt;p&gt;Sure, they could copy and paste the URL, you could provide a “share” button, they could bookmark the page, etc.
However, what happens when:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;The content at the URL changes?&lt;/li&gt;
  &lt;li&gt;They want to attach it to an email?&lt;/li&gt;
  &lt;li&gt;They need an offline copy for archival purposes?&lt;/li&gt;
  &lt;li&gt;They want a hard copy?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When building a PDF export feature for a webapp, you are typically faced with a few options:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Generate the PDF on the backend and serving to the browser (e.g. &lt;a href=&quot;https://www.princexml.com/&quot;&gt;PrinceXML&lt;/a&gt;, &lt;a href=&quot;https://github.com/itext/itext7&quot;&gt;itext&lt;/a&gt;, etc)&lt;/li&gt;
  &lt;li&gt;Dynamically create a PDF in JavaScript (e.g. &lt;a href=&quot;https://github.com/parallax/jsPDF&quot;&gt;JsPDF&lt;/a&gt;, &lt;a href=&quot;https://github.com/diegomura/react-pdf&quot;&gt;react-pdf&lt;/a&gt;, etc)&lt;/li&gt;
  &lt;li&gt;Write the DOM to a canvas and include as an image in the resulting PDF (e.g. using &lt;a href=&quot;https://html2canvas.hertzen.com/&quot;&gt;html2canvas&lt;/a&gt; + JsPDF)&lt;/li&gt;
  &lt;li&gt;Prompting the user to save page as a PDF via the browser print dialog (by calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.print()&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are plenty of great articles &lt;a href=&quot;https://www.smashingmagazine.com/2019/06/create-pdf-web-application/&quot;&gt;elaborating on these options&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;generating-pdfs-in-the-browser-using-windowprint&quot;&gt;Generating PDFs in the browser using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.print&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;The rest of this article will focus on utilising the browser print dialog, and some gotcha’s that you should consider if doing so.&lt;/p&gt;

&lt;p&gt;&lt;span style=&quot;color: darkgreen&quot;&gt;&lt;strong&gt;The primary reason&lt;/strong&gt;&lt;/span&gt; to opt for this approach is that you can benefit from a single code base for both displaying information to users from the web browser as well as exporting a PDF.&lt;/p&gt;

&lt;p&gt;&lt;span style=&quot;color: darkred&quot;&gt;&lt;strong&gt;The main pitfall&lt;/strong&gt;&lt;/span&gt; is that you have much less control over exactly when page-breaks will appear, and in general less control over the layout of the resulting PDF.&lt;/p&gt;

&lt;p&gt;We will consider all of the following:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;#overall-user-experience&quot;&gt;Making the print dialog as transparent as possible to the users&lt;/a&gt; (so it feels as much a part of your application UX rather than part of the browser chrome)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#headers-and-footers&quot;&gt;Adding headers + footers to each page&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;#fitting-user-generated-content&quot;&gt;Fitting complex user generated content to your PDF page&lt;/a&gt; (in particular tables + images) that we have little or no control over (not withstanding XSS)&lt;/li&gt;
  &lt;li&gt;Ensuring all styles and images are loaded prior to initiating a print&lt;/li&gt;
  &lt;li&gt;Page sizing&lt;/li&gt;
  &lt;li&gt;Page breaking (and tables/headers)&lt;/li&gt;
  &lt;li&gt;Isolating styles from the application&lt;/li&gt;
  &lt;li&gt;Managing cross browser quirks&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;overall-user-experience&quot;&gt;Overall user experience&lt;/h3&gt;

&lt;p&gt;A naive approach would be: “Just ask the user to print the page”.&lt;/p&gt;

&lt;p&gt;This experience is a sub-par for many reasons, especially since browsers dropped the notion of a standard “File -&amp;gt; Print” menu in favour of weird little hamburger mystery menus some time ago, making it less likely users actually knwo where the print functionality is found.&lt;/p&gt;

&lt;p&gt;Instead, we can trivially invoke &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.print()&lt;/code&gt; from JavaScript to trigger the same functionality.
This should be done by a nice noticable button somewhere in your web app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TODO: do and don’t images&lt;/strong&gt;. Don’t is screenshot of hamburger menus and “Print” option. Do is a styled button in the webpage (with icon).&lt;/p&gt;

&lt;p&gt;If, for reasons we will discuss below, we opt to render content into a new window for printing, then this button should:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Trigger &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.print()&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;Call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.open()&lt;/code&gt; and render into the new window&lt;/li&gt;
  &lt;li&gt;Size the window to almost hide it behind the modal print dialog&lt;/li&gt;
  &lt;li&gt;Listen to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;window.onafterprint&lt;/code&gt; and close the window when done&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The new window can be made almost transparent by sizing it such that the only thing the user sees is the print dialog.
Without appropriate sizing, users may get confused as to why they are seeing two previews of the content (one from the underlying webpage DOM, and one from the print preview).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TODO: do and don’t images&lt;/strong&gt;. Don’t is a window too large showing two previews, Do is a popup covered by the print dialog.&lt;/p&gt;

&lt;h3 id=&quot;render-into-a-new-window&quot;&gt;Render into a new window&lt;/h3&gt;

&lt;p&gt;The promise of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries/Using_media_queries#targeting_media_types&quot;&gt;CSS media queries&lt;/a&gt; is that we can print a complex web app with only changes to CSS.&lt;/p&gt;

&lt;p&gt;Sometimes, this just doesn’t cut it and we need much more control, such as:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Specific HTML structures to aid with headers and footers&lt;/li&gt;
  &lt;li&gt;Manually manipulating DOM nodes to fit on the screen appropriately&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These can be destructive operations that break the web app. For these reasons, it can be helpful to render your DOM for printing into a new window that can be disposed of once printed.&lt;/p&gt;

&lt;p&gt;We still get to share rendering code, e.g. by rendering the same React components used by your web app into the new window, but we have the freedom of mangling it as we see fit to lay out appropriately on a the page.&lt;/p&gt;

&lt;h3 id=&quot;headers-and-footers&quot;&gt;Headers and footers&lt;/h3&gt;

&lt;p&gt;This will be simple once &lt;a href=&quot;https://www.w3.org/TR/css-page-3/&quot;&gt;CSS Paged Media Module Level 3&lt;/a&gt; is ratified and made avaialble in major browsers.
Until then, we are stuck with ugly workarounds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sidenote:&lt;/strong&gt; There &lt;em&gt;are&lt;/em&gt; rendering engines which understand advanced CSS Paged Media declarations, such as PrinceXML.
However our goal is to utilise the users browser, which means as of writing, we don’t have access to these features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goals:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Show content repeated across the top and bottom of each page&lt;/li&gt;
  &lt;li&gt;Support artibrary number of pages (we normally don’t know how many pages there will be in the resulting document)&lt;/li&gt;
  &lt;li&gt;Prevent obstructing any actual content behind our headers/footers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After searching around, the best approach is embodied in &lt;a href=&quot;https://medium.com/@Idan_Co/the-ultimate-print-html-template-with-header-footer-568f415f6d2a&quot;&gt;this blog post&lt;/a&gt; and variations appear throughout stackoverflow too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Add 2 x &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;position: fixed&lt;/code&gt; elements for the header and footer respectively, and each will be repeated on each page by the browser when printing.&lt;/li&gt;
  &lt;li&gt;To prevent them overlapping other content, reserve space at the top and bottom of each page.&lt;/li&gt;
  &lt;li&gt;Reserve space by warpping content in a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;table&amp;gt;&lt;/code&gt; where the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;thead&amp;gt;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;tfoot&amp;gt;&lt;/code&gt; have a fixed height and are repeated on each page, and the content is within a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;tbody&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;HTML&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&amp;lt;table&amp;gt;
  &amp;lt;thead&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;td class=&quot;header-space&quot;&amp;gt;
        &amp;lt;!-- Empty. Just to reserve space for the &amp;lt;header&amp;gt; --&amp;gt;
      &amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
  &amp;lt;/thead&amp;gt;

  &amp;lt;tbody&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;td&amp;gt;
        &amp;lt;!-- Actual page content goes here --&amp;gt;
      &amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
  &amp;lt;/tbody&amp;gt;

  &amp;lt;tfoot&amp;gt;
    &amp;lt;tr&amp;gt;
      &amp;lt;td class=&quot;footer-space&quot;&amp;gt;
        &amp;lt;!-- Empty. Just to reserve space for the &amp;lt;footer&amp;gt; --&amp;gt;
      &amp;lt;/td&amp;gt;
    &amp;lt;/tr&amp;gt;
  &amp;lt;/tfoot&amp;gt;
&amp;lt;/table&amp;gt;

&amp;lt;header class=&quot;header-content&quot;&amp;gt;
  &amp;lt;!-- Actual header content goes here --&amp;gt;
&amp;lt;/header&amp;gt;

&amp;lt;footer class=&quot;footer-content&quot;&amp;gt;
  &amp;lt;!-- Actual header content goes here --&amp;gt;
&amp;lt;/footer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;CSS&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.header-content, .header-space,
.footer-content, .footer-space {
  height: 100px;
}

.header {
  position: fixed;
  top: 0;
}

.footer {
  position: fixed;
  bottom: 0;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Hopefully you can already see why it may be a good idea to render your printable pages in a new window.
Although possible to use the above HTML in your regular website layout, it will quickly start to cause issues, and we are just getting started!&lt;/p&gt;

&lt;h3 id=&quot;fitting-user-generated-content&quot;&gt;Fitting user generated content&lt;/h3&gt;

&lt;p&gt;Many systems that require exporting PDFs are enterprise systems, often with user generated content.
Such content is normally pasted into a WYSIWYG editor from MS Word.&lt;/p&gt;

&lt;p&gt;&lt;span style=&quot;color: darkred&quot;&gt;&lt;strong&gt;Security Warning:&lt;/strong&gt;&lt;/span&gt; We will leave the notion of &lt;a href=&quot;https://duckduckgo.com/?q=wysiwyg+prevent+xss&quot;&gt;protecting against XSS attacks&lt;/a&gt; for another day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goals:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Fit complex tables, large images, and weird &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;div&lt;/code&gt;s with fixed and large widths to our PDF pages (yes, these do get pasted regularly from weird and wonderful sources by users).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Assumptions:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;We don’t know the structure of these user generated tables.&lt;/li&gt;
  &lt;li&gt;We can’t mess with the layout of the tables (often in enterprise systems, the table layouts can have semantics which change as we resize and shuffle columns around).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One potential solution is actually implemented by webkit itself, called the &lt;a href=&quot;https://github.com/WebKit/WebKit/blob/4f53761ae00714855680bf0b1be0c3e4e442aae8/Source/WebCore/page/PrintContext.cpp#L189-L202&quot;&gt;“shrink factor”&lt;/a&gt;.
It is transparent to web developers, but the print dialog will attempt to shrink the entire document until it fits, but give up after a certain point and then truncate the rest of the content off the page.&lt;/p&gt;

&lt;p&gt;We will build something similar in JavaScript, but instead of shrinking the entire document (unneccesarily making all text small and harder to read), we will just resize the problematic elements.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Figure out how many pixels wide the resulting document will be (depends on the DPI of the current users screen).&lt;/li&gt;
  &lt;li&gt;Find all &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;table&amp;gt;&lt;/code&gt; (and potentially &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;lt;img&amp;gt;&lt;/code&gt; or other) nodes are bigger than this.&lt;/li&gt;
  &lt;li&gt;Resize these until they are no larger than the page width.&lt;/li&gt;
  &lt;li&gt;Allow word breaking to prevent large strings of characters from disappearing&lt;/li&gt;
&lt;/ul&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;word-break: all&lt;/code&gt; to prevent long strings of characters from flowing off the page.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without this, large lines of text such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;aaaaaaaaaaaaaaaaaaaaaaa...&lt;/code&gt; will end up off the page. By allowing the browser to wrap words midway through, we trade off the chance of breaking some words in inopportune places with the benefit of &lt;em&gt;actually getting all our content on the page&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Find all tables which wider than the PDF page, and shrink them.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Gotchas&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Windows defaults to Microsoft Print to PDF. This outputs images. Chrome’s is better.&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>Tempest: Basic 3D</title>
   <link href="https://pserwylo.github.io//2021/08/03/tempest-basic-3d"/>
   <updated>2021-08-03T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2021/08/03/tempest-basic-3d</id>
   <content type="html">&lt;p&gt;Prior to adding enemies, I thought it best to get a working proof of concept third dimension for Tempest.&lt;/p&gt;

&lt;h2 id=&quot;allowing-for-perspective&quot;&gt;Allowing for perspective&lt;/h2&gt;

&lt;p&gt;This was mostly fine, except for the minor hiccup that retrowars &lt;a href=&quot;https://github.com/retrowars/retrowars/commit/384316c63c2e33f5d306778435c196af3ac74052#diff-430e74b10257387cd5713bbef5ad92bf0fb3ea4453106ad13b9f8fd7e1461e78L48&quot;&gt;assumes all games want an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OrthographicCamera&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’m not very happy with my solution of &lt;a href=&quot;https://github.com/retrowars/retrowars/commit/384316c63c2e33f5d306778435c196af3ac74052#diff-430e74b10257387cd5713bbef5ad92bf0fb3ea4453106ad13b9f8fd7e1461e78R39&quot;&gt;passing in a flag if you want perspective&lt;/a&gt;, and will likely refactor that later, but for now it works well.
Here is a discussion on why &lt;a href=&quot;https://www.martinfowler.com/bliki/FlagArgument.html&quot;&gt;flag arguments&lt;/a&gt; are normally not the solution to a problem you face.&lt;/p&gt;

&lt;p&gt;Here are the results:&lt;/p&gt;

&lt;video class=&quot;border&quot; style=&quot;max-width: 960; max-height: 512&quot; controls=&quot;&quot;&gt;
    &lt;source src=&quot;/assets/videos/tempest/tempest-initial-3d.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;

&lt;h2 id=&quot;offsetting-the-perspective&quot;&gt;Offsetting the perspective&lt;/h2&gt;

&lt;p&gt;After patting myself on the back about how it mostly looked like the YouTube videos of original Tempest, I quickly noticed that the perspective in the original game is actually offset a little.
The camera doesn’t look directly down the centre of the level, but starts a bit below centre.&lt;/p&gt;

&lt;p&gt;After accounting for this by translating the camera in my version down slightly, it really is starting to shape up nicely.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/tempest/tempest-perspective.png&quot; alt=&quot;Screenshot of work in progress perspective in Tempest&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/tempest/tempest-perspective-2.png&quot; alt=&quot;Screenshot of work in progress perspective in Tempest&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;

&lt;p&gt;Next I will tighten up some of the measurements (currently the depth is just arbitrarily chosen as a fixed value).
After that, I think shooting from the players current position, then the addition of enemies.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Tempest: Basic progress</title>
   <link href="https://pserwylo.github.io//2021/08/02/tempest-basic-progress"/>
   <updated>2021-08-02T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2021/08/02/tempest-basic-progress</id>
   <content type="html">&lt;p&gt;In order to start making progress on this game, the initial work will:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Ignore the third dimension&lt;/li&gt;
  &lt;li&gt;Ignore enemies&lt;/li&gt;
  &lt;li&gt;Focus on the break down of levels into segments in a loop&lt;/li&gt;
  &lt;li&gt;Allow the player to move around these segments&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I must confess that I’ve never actually played before, but I do really like the videos I’ve seen, so please go easy on me :)
My reference point for how the levels look and how the game is played is this YouTube video:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=jfaCrdBABUY&quot;&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/tempest/tempest-youtube.png&quot; alt=&quot;Link to YouTube video of Tempest gameplay&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;first-three-levels&quot;&gt;First three levels&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/retrowars/retrowars/commit/7d394a163803f4354aa0acdd65db6039f2c6995d&quot;&gt;first&lt;/a&gt; &lt;a href=&quot;https://github.com/retrowars/retrowars/commit/aa54ae7b55073e7b46eef811c758e3f92644e785&quot;&gt;three&lt;/a&gt; &lt;a href=&quot;https://github.com/retrowars/retrowars/commit/34ce2d14e12e23a0f52798ccf1484df7b48203d4&quot;&gt;commits&lt;/a&gt; were to add the &lt;a href=&quot;https://github.com/retrowars/retrowars/commit/7d394a163803f4354aa0acdd65db6039f2c6995d#diff-2c109f1921ae52b4b70e9979350c924b9e92d9468cd4e64a75e9e84d0a02f191R14&quot;&gt;first&lt;/a&gt; &lt;a href=&quot;https://github.com/retrowars/retrowars/commit/aa54ae7b55073e7b46eef811c758e3f92644e785#diff-2c109f1921ae52b4b70e9979350c924b9e92d9468cd4e64a75e9e84d0a02f191R39&quot;&gt;three&lt;/a&gt; &lt;a href=&quot;https://github.com/retrowars/retrowars/commit/34ce2d14e12e23a0f52798ccf1484df7b48203d4#diff-2c109f1921ae52b4b70e9979350c924b9e92d9468cd4e64a75e9e84d0a02f191R77&quot;&gt;levels&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/retrowars/retrowars/commit/7d394a163803f4354aa0acdd65db6039f2c6995d#diff-2c109f1921ae52b4b70e9979350c924b9e92d9468cd4e64a75e9e84d0a02f191R9&quot;&gt;Each level is broken into a list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Segment&lt;/code&gt;s&lt;/a&gt;, where each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Segment&lt;/code&gt; represents a start and an end point on the screen using a pair of libgdx &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vector2&lt;/code&gt;s:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TempestGameState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;level:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;segments:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Segment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;,&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Segment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;start:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Vector2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;end:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Vector2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And then each level is hand crafted using whatever necessary (e.g. hard coded start and end points for each segment in the rectangular second and third levels, but trigonometry in the first “round” level).&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/tempest/tempest-level-1.png&quot; alt=&quot;Screenshto of initial commit&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/tempest/tempest-level-2.png&quot; alt=&quot;Screenshto of initial commit&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/tempest/tempest-level-3.png&quot; alt=&quot;Screenshto of initial commit&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;player-position-and-movement&quot;&gt;Player position and movement&lt;/h2&gt;

&lt;p&gt;By &lt;a href=&quot;https://github.com/retrowars/retrowars/commit/7a4b2bdfe57653de1fc3fd1506f1af9c0487144a#diff-2c109f1921ae52b4b70e9979350c924b9e92d9468cd4e64a75e9e84d0a02f191R12&quot;&gt;recording which segment the current player is on&lt;/a&gt;, it becomes straightforward to:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/retrowars/retrowars/commit/7a4b2bdfe57653de1fc3fd1506f1af9c0487144a#diff-858e4d54a1131cd1f2f208bbcde55df53e211eab84c636550212e2ceb184cae8R93&quot;&gt;Highlight this segment differently during rendering&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/retrowars/retrowars/commit/7a4b2bdfe57653de1fc3fd1506f1af9c0487144a#diff-858e4d54a1131cd1f2f208bbcde55df53e211eab84c636550212e2ceb184cae8R47&quot;&gt;Respond to input&lt;/a&gt; by changing the segment to the next one (with the exception that we need to &lt;a href=&quot;https://github.com/retrowars/retrowars/commit/7a4b2bdfe57653de1fc3fd1506f1af9c0487144a#diff-858e4d54a1131cd1f2f208bbcde55df53e211eab84c636550212e2ceb184cae8R73&quot;&gt;support wrapping around the loop&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;video class=&quot;border&quot; style=&quot;max-width: 960; max-height: 512&quot; controls=&quot;&quot;&gt;
    &lt;source src=&quot;/assets/videos/tempest/tempest-initial-rotation.mp4&quot; type=&quot;video/mp4&quot; /&gt;
&lt;/video&gt;

&lt;p&gt;The player segment is recorded in the game state as a reference to one of the level segments.
We could equally store an index to the segment in question, but this worked well for the Snake game and made a lot of comparisons much simpler.&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;TempestGameState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;worldWidth:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;worldHeight:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;nl&quot;&gt;level:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;makeThirdLevel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;worldWidth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;worldHeight&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;playerSegment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;segments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The input handling is taken from the tetris game, whereby we need to ensure that only one keypress is registered each frame.
Otherwise, on faster devices, the game will respond too quickly.&lt;/p&gt;

&lt;p&gt;Therefore, instead of asking whether a button is pressed at the start of each frame (as we do with Asteroids), we instead record a button status when the button is first pressed, and then clear that status each frame after processing it:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TempestGameState&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;moveCounterClockwise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ButtonState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Unpressed&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;moveClockwise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ButtonState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Unpressed&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TempestGameScreen&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!!.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;TempestSoftController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Buttons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;MOVE_COUNTER_CLOCKWISE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;moveCounterClockwise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;moveCounterClockwise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ButtonState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Unpressed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ButtonState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;JustPressed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ButtonState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Held&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;moveCounterClockwise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ButtonState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Unpressed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!!.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;TempestSoftController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Buttons&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;MOVE_CLOCKWISE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;moveClockwise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;moveClockwise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ButtonState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Unpressed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ButtonState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;JustPressed&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ButtonState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Held&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;moveClockwise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ButtonState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Unpressed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;})&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;updateGame&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;delta:&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Float&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;movePlayer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;resetInput&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;movePlayer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentIndex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;segments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;playerSegment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;moveClockwise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ButtonState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;JustPressed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;playerSegment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;segments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;currentIndex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;segments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;moveCounterClockwise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ButtonState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;JustPressed&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;playerSegment&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;segments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;segments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;currentIndex&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;segments&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;resetInput&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;moveCounterClockwise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ButtonState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Unpressed&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;moveClockwise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ButtonState&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;Unpressed&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;

&lt;p&gt;Next will either be the addition of firing from the players current position, or the addition of a 3rd dimension (which will make it easier to see if the firing logic is working as expected).&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Tempest: Initial skeleton</title>
   <link href="https://pserwylo.github.io//2021/08/01/tempest-initial-skeleton"/>
   <updated>2021-08-01T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2021/08/01/tempest-initial-skeleton</id>
   <content type="html">&lt;h2 id=&quot;first-steps-adding-a-blank-game-skeleton&quot;&gt;First steps: Adding a blank game skeleton&lt;/h2&gt;

&lt;p&gt;Before getting into the specifics of Tempest, I thought I’d quickly review what is involved in adding a new game.&lt;/p&gt;

&lt;p&gt;This can be seen in commit &lt;a href=&quot;https://github.com/retrowars/retrowars/commit/4d7d89d2dcf0885a84aefeec5d61914dab1c098c&quot;&gt;4d7d89d2&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/retrowars/retrowars/commit/4d7d89d2dcf0885a84aefeec5d61914dab1c098c&quot;&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/tempest/commit-initial-skeleton.png&quot; alt=&quot;Screenshto of initial commit&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is the minimum required to get to this point:&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/tempest/tempest-game-select.png&quot; alt=&quot;Game select screen with the new Tempest game shown&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/tempest/tempest-empty-game.png&quot; alt=&quot;Empty Tempest game screenshot&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;code-review-of-these-changes&quot;&gt;Code review of these changes&lt;/h2&gt;

&lt;p&gt;I’m a big fan of code reviews. It is a wonderful way to mentor others, and an even better way to learn yourself.
Although reviewing your own code kind of defeats the point, I hope that doing so in a public forum like this will let me be a bit more reflective, critical, and constructive.&lt;/p&gt;

&lt;h3 id=&quot;first-thoughts&quot;&gt;First thoughts&lt;/h3&gt;

&lt;p&gt;I don’t like the size of that commit.&lt;/p&gt;

&lt;p&gt;Well, it isn’t so much the size, but rather the random locations with which code needs to be added.
If it is non-obvious to myself as the original author, it does not bode well for others in the community attempting to implement games.&lt;/p&gt;

&lt;p&gt;Here are some of the things I find odd and should perhaps refactor in the future…&lt;/p&gt;

&lt;h3 id=&quot;adding-a-controller&quot;&gt;Adding a controller&lt;/h3&gt;

&lt;p&gt;Most games require a controller to be shown on screen. The steps to add a controller for Tempest are:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Adding an actor to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OptionsScreen.kt&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;addActor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;nc&quot;&gt;IconButton&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;skin&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Games&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;tempest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sprites&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;Gdx&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;postRunnable&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;screen&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ControllerSelectScreen&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;Games&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;tempest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;nc&quot;&gt;TempestSoftController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;layouts&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TempestSoftController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;index&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;uiAssets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Why do we need to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;addActor&lt;/code&gt; on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OptionsScreen&lt;/code&gt;? Why do we need to create a button? Why do we need to hard code a reference to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Games.tempest&lt;/code&gt; twice and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TempestSoftController&lt;/code&gt; twice? Why does every game that wants to have a controller need to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Gdx.app.postRunnable { game.screen = ControllerSelectScreen(...) }&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Create a controller and manually add to HUD in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;TempestGameScreen&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;TempestSoftController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Options&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getSoftController&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nc&quot;&gt;Games&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;tempest&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;game&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;uiAssets&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;init&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;addGameOverlayToHUD&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getActor&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Proposed improvement&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The base &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GameScreen&lt;/code&gt; base class should have an open function which optionally returns a controller:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;open fun getController(): SoftController? = null
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OptionsScreen&lt;/code&gt; can then query the (already present) list of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Games.allSupported&lt;/code&gt; (which we had to populate as part of the initial game adding process) and ask each one for a controller.
Those that return a controller will end up with an entry in the options screen.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;GameScreen&lt;/code&gt; can then use the fact that a controller exists to decide whether or not to add one to the HUD instead of relying on the base class to have to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;addGameOverlayToHUD(controller.getActor())&lt;/code&gt; at some point.&lt;/p&gt;

&lt;p&gt;This may require refactoring the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SoftController&lt;/code&gt; class hierarchy so that it can be created and queried without having to instantiate an actor or specify which controller layout to use.
That is to say, the options screen should be able to know that a game has a controller without having to create a specific instantiation of one.&lt;/p&gt;

&lt;h3 id=&quot;displaying-a-message-at-the-start-of-the-game&quot;&gt;Displaying a message at the start of the game&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;showMessage()&lt;/code&gt; during &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;init&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;init {
    showMessage(&quot;Shoot the enemies&quot;, &quot;Don't let them touch you&quot;)
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is easy to do, and only needs a single line to be added.
However, people adding new games have to remember to do this.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Proposed improvement&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Force the implementing game to provide these messages to the constructor.
That way, the compiler will tell the game designer that these are required pieces of information to make the game behave like all other games.&lt;/p&gt;

&lt;h2 id=&quot;next-steps&quot;&gt;Next steps&lt;/h2&gt;

&lt;p&gt;I will have a go at implementing the proposed feedback prior to starting Tempest properly.
Hopefully it shouldn’t take too long and then we can progress with getting the next game done.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Let the community decide the next retro game</title>
   <link href="https://pserwylo.github.io//2021/08/01/let-the-community-decide-the-next-retro-game"/>
   <updated>2021-08-01T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2021/08/01/let-the-community-decide-the-next-retro-game</id>
   <content type="html">&lt;h2 id=&quot;let-the-community-decide&quot;&gt;Let the community decide&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/retrowars/retrowars&quot;&gt;retrowars&lt;/a&gt; project began with me implementing the games I was personally most interested in seeing: clones of Asteroids, Missile Command, Tetris, and Snake.
Once these were completed, I had no preference over what to implement next (though I’m very keen to keep adding games!).&lt;/p&gt;

&lt;p&gt;To encourage those in the community to participate and perhaps even grow a little community, the game includes a built-in link for “More games”.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/tempest/master-game-select.png&quot; alt=&quot;Retrowars game select screen, including option of 'more soon'&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/tempest/vote.png&quot; alt=&quot;Retrowars screen explaining how to vote for a new game to be made&quot; /&gt;&lt;/p&gt;

&lt;p&gt;This directs the player to a GitHub link where they can &lt;a href=&quot;https://github.com/retrowars/retrowars/issues?q=is%3Aissue+is%3Aopen+label%3Agame-proposal&quot;&gt;view proposed games, and vote for the game they would like to see next&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/tempest/proposed-games.png&quot; alt=&quot;GitHub issue page listing proposed games&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, people are encouraged to recommend their own favourite game to be implemented by &lt;a href=&quot;https://github.com/retrowars/retrowars/issues/new/choose&quot;&gt;submitting a New Game Proposal&lt;/a&gt; for others to vote on.&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/tempest/github-issue-templates.png&quot; alt=&quot;GitHub issue templates, including option to propose a new game&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;next-game-tempest&quot;&gt;Next game: Tempest&lt;/h2&gt;

&lt;p&gt;Now is the time where I am available to begin contributing a new game to retrowars.
At the time I decide this, &lt;a href=&quot;https://github.com/retrowars/retrowars/issues/10&quot;&gt;Tempest&lt;/a&gt; was the game which had the most votes (even though that is no longer the case).&lt;/p&gt;

&lt;p&gt;The next post will go over what is required to get an initial skeleton of the game up and running.
Following that, subsequent posts will document the journey to getting a working version of Tempest into retrowars.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>WebSocket game server in Kotlin using Ktor</title>
   <link href="https://pserwylo.github.io//2021/07/28/websocket-game-server"/>
   <updated>2021-07-28T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2021/07/28/websocket-game-server</id>
   <content type="html">&lt;h2 id=&quot;free-vs-commercial-software-infrastructure&quot;&gt;Free vs commercial software infrastructure&lt;/h2&gt;

&lt;p&gt;As explained in a &lt;a href=&quot;/2021/07/03/sustainable-server-infra-for-open-source-games&quot;&gt;previous post&lt;/a&gt;, free software games that have infrastructure requirements are slightly different then their commercial counterparts.
Normally, free software projects don’t cost anything other then the time (and sometimes sanity!) of the contributors.
However, as soon as there are requirements on infrastructure, such as a multiplayer game, this normally results in a financial cost.
Given that these projects are usually a labour of love and don’t earn money other than donations, this cost can be significant, depending on the popularity of the project and therefore the traffic involved.
Commercial games can cover this cost, but free software projects often cannot.&lt;/p&gt;

&lt;h2 id=&quot;preparing-retrowars-for-online-multiplayer&quot;&gt;Preparing retrowars for online multiplayer&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://github.com/retrowars&quot;&gt;retrowars&lt;/a&gt; project is a free software multiplayer Android game.
Earlier versions only supported local network multiplayer, which has no infrastructure requirements, but the goal was always to support true multiplayer via the public internet.
This requirement naturally means server infrastructure is required (or elaborate P2P setups which is beyond the scope of this game).&lt;/p&gt;

&lt;p&gt;Porting of the local network server to a public internet server was very straightforward, the same code worked essentially as is without modification.&lt;/p&gt;

&lt;p&gt;Specifically, it made use of the &lt;a href=&quot;https://github.com/EsotericSoftware/kryonet&quot;&gt;KryoNet&lt;/a&gt; library which does a great job of:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Serializing and deserializing Java objects using an efficient binary protocol.&lt;/li&gt;
  &lt;li&gt;Discovering servers on the local network&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;a-desire-for-http-via-websockets&quot;&gt;A desire for HTTP (via WebSockets)&lt;/h2&gt;

&lt;p&gt;Given the goal of letting people run their own server, the thought process was to make sure that the server can be deployed on commonly available infrastructure as a service.
A popular approach for deploying Java apps is Heroku, which allows people to deploy with a single command, and not have to worry about setting up a virtual machine, installing Java, etc.&lt;/p&gt;

&lt;p&gt;When trying to deploy the KryoNet version of the server, it was immediately apparent that using non-HTTP traffic for networking would not work with Heroku.
Furthermore, this experience also made it clear that deploying the server to non-Heroku environments may also cause issues, as many firewalls only allow HTTP traffic through.&lt;/p&gt;

&lt;p&gt;This resulted in a refactoring of the retrowars server to use HTTP and websockets instead of KryoNet.&lt;/p&gt;

&lt;h3 id=&quot;websockets-json-ktor-jmdns-and-coroutines&quot;&gt;WebSockets, JSON, Ktor, JmDNS, and Coroutines&lt;/h3&gt;

&lt;p&gt;Converting the server from KryoNet meant replacing:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/EsotericSoftware/kryo&quot;&gt;Kryo serialization&lt;/a&gt; -&amp;gt; &lt;a href=&quot;https://github.com/google/gson&quot;&gt;GSON&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/EsotericSoftware/kryonet&quot;&gt;KryoNet networking&lt;/a&gt; -&amp;gt; &lt;a href=&quot;https://ktor.io/docs/websocket.html&quot;&gt;Ktor WebSockets&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/EsotericSoftware/kryonet#lan-server-discovery&quot;&gt;KryoNet local network discovery&lt;/a&gt; -&amp;gt; &lt;a href=&quot;https://github.com/jmdns/jmdns&quot;&gt;JmDNS&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/EsotericSoftware/kryonet#threading&quot;&gt;KryoNet threads&lt;/a&gt; -&amp;gt; Ktor coroutines&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This was my first experience with Kotlin coroutines, which took a lot of reading and experimenting with before making substantial progress (and I’m still not 100% happy with my usage of them at this point - though I’m sure it will improve with experience).&lt;/p&gt;

&lt;h3 id=&quot;websocket-performance&quot;&gt;WebSocket performance&lt;/h3&gt;

&lt;p&gt;Many game development blogs discuss best practice with regards to networking technologies.
TCP is a bit slower than UDP, and there is not much discussion of using WebSockets other than for web-based HTML games.
In practice, it seems that other than the overhead of establishing a HTTP connection and upgrading it to a WebSocket connection, the actual sending of messages should be the same as sending bytes directly down a socket.
The retrowars game does not have high performance networking requirements that something such as an FPS may require, so this is even less of an issue.
In practice, the performance of WebSockets is perfectly sufficient for the retrowars game.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Sustainable server infrastructure for open source games</title>
   <link href="https://pserwylo.github.io//2021/07/03/sustainable-server-infra-for-open-source-games"/>
   <updated>2021-07-03T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2021/07/03/sustainable-server-infra-for-open-source-games</id>
   <content type="html">&lt;p&gt;Lets design an open source multiplayer game server arcitecture that is:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Resiliant to the main developer being unable to perpetually maintain an online server.&lt;/li&gt;
  &lt;li&gt;Able to scale up as demand increases, by allowing community members to contribute and run game servers.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Specficially, this is for the &lt;a href=&quot;https://github.com/retrowars/retrowars&quot;&gt;Super Retro Mega Wars&lt;/a&gt; game.
Even more specifically, I want people to be able to play retro games in realtime against eachother long after I have the capacity or finances to maintain an online server to facilitate multiplayer gameplay.&lt;/p&gt;

&lt;h2 id=&quot;v10---basic-multiplayer-via-a-local-network&quot;&gt;v1.0 - Basic multiplayer via a local network&lt;/h2&gt;
&lt;p&gt;Multiplayer can be as simple or difficult as we want. Lets assume simple, because this article is not about minimizing latency, detecting and preventing cheaters, etc.
In our case, lets assume the game design doesn’t care all that much about latency, and it doesn’t really care about preventing cheaters.&lt;/p&gt;

&lt;p&gt;These assumptions mean that we can create a proof of concept whereby one player opens up a server socket, and another connects a client socket to this.&lt;/p&gt;

&lt;p&gt;Although it will work well on a LAN, this doesn’t allow people around the world to connect to eachother due to NAT and other considerations.
For that, we’ll need one or more publicly available servers on the internet.&lt;/p&gt;

&lt;h2 id=&quot;v20---central-internet-based-game-servers&quot;&gt;v2.0 - Central internet based game servers&lt;/h2&gt;
&lt;p&gt;The local networking code can be extracted into a server-side application with little modification.
Hosting this is not particularly difficult either, we deploy the server-side application on a VPS somewhere and embed the URL of this server into the application.&lt;/p&gt;

&lt;p&gt;However, what happens if the server goes down? The IP changes? The domain is not renewed? The maintainer gets hit by a bus?
Under each of these scenarios, we are left with a game that has a perpetually broken multiplayer feature.
Every person who discovers this game and installs it, will find that they can only play single player and when they try to play multiplayer, it will error.&lt;/p&gt;

&lt;h2 id=&quot;v30-a-more-robust-open-source-multiplayer-architecture&quot;&gt;v3.0 A more robust open source multiplayer architecture&lt;/h2&gt;

&lt;h3 id=&quot;v31---players-manually-enter-server-details&quot;&gt;v3.1 - Players manually enter server details&lt;/h3&gt;
&lt;p&gt;The simplest technical solution is to let people run their own servers and let players manually enter details of a server to play.
How the player gets the server details is not our problem.
Perhaps there are wiki pages or forum posts that enumerate game servers.&lt;/p&gt;

&lt;p&gt;While technically simple, this is not a plesant user experience.&lt;/p&gt;

&lt;h3 id=&quot;v32---hard-code-a-list-of-servers-in-the-game&quot;&gt;v3.2 - Hard code a list of servers in the game&lt;/h3&gt;
&lt;p&gt;To improve the user experience, we want players to be able to open the game and play online with the click of a button.
For this, the game needs to know which servers are available.
This can be done by hard coding a list of servers in the game.
When new servers become available or others are decomissioned, a new version of the game is published.&lt;/p&gt;

&lt;p&gt;The problem here is that those who have not updated the game will have a stale list of servers.&lt;/p&gt;

&lt;h3 id=&quot;v33---online-list-of-servers-managed-by-game-maintainers&quot;&gt;v3.3 - Online list of servers managed by game maintainers&lt;/h3&gt;
&lt;p&gt;Instead of hardcoding a list of servers, the game can refer to a URL that contains a list of servers.
As new servers are brought to the attention of the maintainers, the list can be updated.&lt;/p&gt;

&lt;p&gt;This ticks almost all the boxes, but the single point of failure is still the game maintainers.
If they stop maintaining the list of servers, then the game will very quickly stop working.&lt;/p&gt;

&lt;h4 id=&quot;v34---online-list-of-servers-managed-by-the-community&quot;&gt;v3.4 - Online list of servers managed by the community&lt;/h4&gt;
&lt;p&gt;Finally, we want this online list of servers to be maintainable by the community, so that in the case that the maintainers are no longer able to manage it, community members can do so.&lt;/p&gt;

&lt;p&gt;This not only solves the key point of failure issue, but also fits very well with the ethos of free and open source software projects.&lt;/p&gt;

&lt;h2 id=&quot;community-maintained-server-list&quot;&gt;Community maintained server list&lt;/h2&gt;
&lt;p&gt;The way we will do this for Super Retro Mega Wars is to have:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A JSON file with details of publicly available servers.&lt;/li&gt;
  &lt;li&gt;Host this on a GitHub Pages URL associated with the repository (e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;https://retrowars.github.io/servers.json&lt;/code&gt;).&lt;/li&gt;
  &lt;li&gt;Encourage community contributors on the GitHub Pages repository.&lt;/li&gt;
  &lt;li&gt;Document a process for proposing changes to the server list.&lt;/li&gt;
  &lt;li&gt;Automatically publish the server list when merge requests are accepted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This way, if the original maintainers of the game are unable to continue maintaining the server list, then the community is stil able to ensure that multiplayer games are supported.&lt;/p&gt;

&lt;h2 id=&quot;community-run-servers&quot;&gt;Community run servers&lt;/h2&gt;
&lt;p&gt;In order for the community to be able to ensure continued online play is possible, it needs to be as simple as possible to run your own game server.
Some of the decisions Super Retro Mega Wars is making in this area will be discussed in a separate blog post.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>LCA 2018: F-Droid: The private, secure, free and open app store for Android</title>
   <link href="https://pserwylo.github.io//2018/01/25/lca-2018-f-droid-the-private-secure-free-and-open-app-store-for-android"/>
   <updated>2018-01-25T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2018/01/25/lca-2018-f-droid-the-private-secure-free-and-open-app-store-for-android</id>
   <content type="html">&lt;p&gt;To &lt;a href=&quot;https://f-droid.org/2018/02/02/linux-conf-au-fdroid-presentation.html&quot;&gt;quote F-Droid’s Licaon Kter&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;The 45 minutes long talk covers how F-Droid come to be, focussing on the client, its security model, and how it is being used to ensure everyone (look out for the “collateral freedom” bit) is able to access high quality open source apps, using tools developed to make it easier for anybody to setup and run their own curated app store.&lt;/p&gt;

  &lt;p&gt;This is a great primer for new users giving them a behind the scenes view on how apps come to be, from a source code repo to their Android devices.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2 id=&quot;video-of-presentation&quot;&gt;Video of Presentation&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=y3zVcYuE-WI&quot;&gt;YouTube link&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;slides-used-in-presentation&quot;&gt;Slides used in Presentation&lt;/h2&gt;

&lt;p&gt;Slides are available at &lt;a href=&quot;https://pserwylo.gitlab.io/fdroid-lca-2018&quot;&gt;https://pserwylo.gitlab.io/fdroid-lca-2018&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>PhD: Eliciting Bayesian Networks via Online Surveys: A New Approach to Knowledge Elicitation</title>
   <link href="https://pserwylo.github.io//2016/04/01/phd"/>
   <updated>2016-04-01T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2016/04/01/phd</id>
   <content type="html">&lt;p&gt;In April 2016, I was awarded a PhD in computer science for my dissertation entitled “Eliciting Bayesian Networks via Online Surveys: A New Approach to Knowledge Elicitation”.&lt;/p&gt;

&lt;p&gt;For those interested, you can &lt;a href=&quot;../../../assets/documents/Peter Serwylo PhD.pdf&quot;&gt;download the (cc-by-sa) PDF&lt;/a&gt;.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Make your writing beautiful</title>
   <link href="https://pserwylo.github.io//2013/08/12/make-your-writing-beautiful"/>
   <updated>2013-08-12T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2013/08/12/make-your-writing-beautiful</id>
   <content type="html">&lt;p&gt;This is a seminar I gave to fellow research students about some things you should consider when writing articles, to make them look visually beautiful. It was given for the first “HDR Lunchtime Seminar” at Caufield Monash. Apologies for any typos or other errors, I did it in a short amount of time because I was busy with some other stuff around the same time.&lt;/p&gt;

&lt;p&gt;If you are interested in this topic, you may also be interested in the talk I gave at the Open Source Developers Conference, “Please, no more gauges: How NOT to visualize data” (video and slides are available).&lt;/p&gt;

&lt;h2 id=&quot;slides&quot;&gt;Slides&lt;/h2&gt;
&lt;p&gt;You can &lt;a href=&quot;/archive/talks/monash/writing/pres.html&quot;&gt;view the slides online&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;cheatsheet&quot;&gt;Cheatsheet&lt;/h2&gt;
&lt;p&gt;You can &lt;a href=&quot;/archive/talks/monash/writing/content/cheatsheet/cheatsheet.pdf&quot;&gt;download the cheatsheet as a pdf&lt;/a&gt; or &lt;a href=&quot;/archive/talks/monash/writing/cheatsheet/html/cheatsheet.html&quot;&gt;view it online&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;software&quot;&gt;Software&lt;/h2&gt;
&lt;p&gt;After the talk, a few people asked about the software I used when writing. Here are some links for anybody who is interested. All the software is open source, and cross platform.&lt;/p&gt;

&lt;p&gt;As is often the case with new/different software, you may be familiar with existing alternatives:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;LyX -&amp;gt; Microsoft Office&lt;/li&gt;
  &lt;li&gt;Inkscape -&amp;gt; Adobe Illustrator&lt;/li&gt;
  &lt;li&gt;GIMP -&amp;gt; Adobe Photoshop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Due to the fact we are talking about pretty complex pieces of software, you may find these alternatives clunky at first. I strongly believe that this is related to familiarity with other “ways of doing things”. I have met professional graphic designers who use Inkscape and GIMP instead of their Adobe alternatives. Also, I was trained in Photoshop and Illustrator in high school and uni, but am now pretty good in GIMP and Inkscape. Now I’ve learnt how to use them (though not as well as professionals would), they are just as powerful and intuitive as the alternatives for the tasks I use them for.&lt;/p&gt;

&lt;h3 id=&quot;lyx-word-processor&quot;&gt;LyX (word processor)&lt;/h3&gt;
&lt;p&gt;A “What You See Is What You Mean” word processor, which outputs articles to PDF via LaTeX. I think this is a good introduction to the beautiful output of LaTeX for Microsoft Word or Libre/OpenOffice users. It is cross platform, and has great documentation. This is the software I am writing my thesis and all of my articles in, and although every now and then I’ll find one or two things a little tricky, it is by and large a much more pleasurable experience than writing in Word or LibreOffice.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.lyx.org/Download&quot;&gt;Download LyX&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://wiki.lyx.org/LyX/LyXHelpDocuments&quot;&gt;Lyx Documentation&lt;/a&gt;: LyX is different in that the best documentation is not on a webpage, but rather in a LyX document accessible from the Help menu. You can either view it in LyX, or output it to a PDF once you’ve opened it.&lt;/p&gt;

&lt;h3 id=&quot;inkscape-vector-graphics-editor&quot;&gt;Inkscape (vector graphics editor)&lt;/h3&gt;
&lt;p&gt;Inkscape is the open source alternative to Illustrator. It may not seem as powerful at first, but it has all of the features that I used in illustrator, and is great for making beautiful diagrams for your writing.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://inkscape.org/download/?lang=en&quot;&gt;Download Inkscape&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;the-gimp-bitmap-photo-editor&quot;&gt;The GIMP (bitmap photo editor)&lt;/h3&gt;
&lt;p&gt;The GIMP is the open source alternative to Photoshop. I found the transition from Photoshop -&amp;gt; GIMP much easier than Illustrator -&amp;gt; Inkscape, because it shares a very similar paradigm for working.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.gimp.org/downloads/&quot;&gt;Download The GIMP&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>OSDC 2012: Building a free(ish) MAME cabinet</title>
   <link href="https://pserwylo.github.io//2012/12/11/osdc-2012-building-a-freeish-mame-cabinet"/>
   <updated>2012-12-11T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2012/12/11/osdc-2012-building-a-freeish-mame-cabinet</id>
   <content type="html">&lt;p&gt;This year I again attended the &lt;a href=&quot;https://web.archive.org/web/20130513112704/http://2012.osdc.com.au/talks/building-freeish-mame-cabinet&quot;&gt;Open Source Developers Conference&lt;/a&gt;. The talk I gave was another light hearted one, this time about building a MAME Cabinet using reclaimed rubbish. A huge props to the organising team for an awesome conference.&lt;/p&gt;

&lt;h2 id=&quot;slides-used-in-presentation&quot;&gt;Slides used in Presentation&lt;/h2&gt;
&lt;p&gt;Please click the image below to view the presentation in your browser (made using &lt;a href=&quot;https://github.com/adamzap/landslide&quot;&gt;Landslide&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/archive/talks/osdc/2012/pres.html&quot;&gt;&lt;img title=&quot;Home made MAME table&quot; src=&quot;/archive/talks/osdc/2012/images/product-5.jpg&quot; alt=&quot;Home made MAME table&quot; width=&quot;600&quot; height=&quot;400&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;resources&quot;&gt;Resources&lt;/h2&gt;

&lt;p&gt;These are the resources I placed on the final slide.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/pserwylo/mame-controller&quot;&gt;Wireless controller client/server&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://web.archive.org/web/20130103064946/http://www.maximumpc.com/article/features/how_build_kickass_mame_arcade_cabinet_old_pc&quot;&gt;Tutorial on building a MAME cabinet&lt;/a&gt; (one of many)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://web.archive.org/web/20140326030924/arcadecontrols.com/arcade_examples.php&quot;&gt;Completed cabinets (Hall of fame)&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://bit.ly/1VyeSE&quot;&gt;What not to do&lt;/a&gt; (language warning)&lt;/li&gt;
&lt;/ul&gt;
</content>
 </entry>
 
 <entry>
   <title>OSDC 2011: Wii Homebrew: Running and writing software for the Wii</title>
   <link href="https://pserwylo.github.io//2011/11/21/osdc-2011-wii-homebrew-running-and-writing-software-for-the-wii"/>
   <updated>2011-11-21T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2011/11/21/osdc-2011-wii-homebrew-running-and-writing-software-for-the-wii</id>
   <content type="html">&lt;p&gt;I &lt;a href=&quot;https://web.archive.org/web/20120115125428/http://2011.osdc.com.au/schedule/#wiihomebrewrunnin&quot;&gt;spoke&lt;/a&gt; at the &lt;a href=&quot;https://web.archive.org/web/20120114223236/http://2011.osdc.com.au/&quot;&gt;OSDC 2011&lt;/a&gt; in Canberra, Australia.
The talk was about writing and using &lt;a href=&quot;http://wiibrew.org/wiki/Glossary#H&quot;&gt;homebrew&lt;/a&gt; software on the Wii. I got some great feedback after the talk, and was happy to see so many people interested in the idea.
I was also pleased to see how many people already have a modded Wii.
Hopefully I was able to give a few people some tips.&lt;/p&gt;

&lt;h2 id=&quot;video-of-presentation&quot;&gt;Video of Presentation&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://youtu.be/D03wx9Uz8IY&quot;&gt;YouTube link&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;presentation-software&quot;&gt;Presentation Software&lt;/h2&gt;

&lt;p&gt;Part of my preparation for the talk was to hack together some presentation software to run on the Wii.
This allowed me to give the actual presentation on the Wii.
I called it Priijector (because every piece of software for the Wii has to have a double ii somewhere, and everything homebrew related has to include a pun).
It will read a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pres.txt&lt;/code&gt; file in a custom (and hacky) markup to define slides and transitions.&lt;/p&gt;

&lt;p&gt;You can &lt;a href=&quot;https://github.com/pserwylo/Priijector&quot;&gt;get the code from GitHub&lt;/a&gt; (GPLv3 license).&lt;/p&gt;

&lt;h2 id=&quot;slides-used-in-presentation&quot;&gt;Slides used in Presentation&lt;/h2&gt;

&lt;p&gt;Please &lt;a href=&quot;/archive/talks/osdc/2011/pres.pdf&quot;&gt;click the image below&lt;/a&gt; to download the PDF of the presentation (3mb). The quality is a little poor, because I had to take screenshots of each slide from within the Dolphin emulator at a resolution of 640×480.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/archive/talks/osdc/2011/pres.pdf&quot;&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/osdc-2011-thumb.png&quot; alt=&quot;Screenshot from slide deck showing icons of various exploits&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>PhD confirmation seminar</title>
   <link href="https://pserwylo.github.io//2011/08/01/phd-confirmation-seminar"/>
   <updated>2011-08-01T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2011/08/01/phd-confirmation-seminar</id>
   <content type="html">&lt;p class=&quot;legacy-warning&quot;&gt;If this is of interest to you, it is worth noting this is &lt;em&gt;not&lt;/em&gt; the topic of my final PhD. That pivoted quite substantially between this presentation and the final thesis in 2016.&lt;/p&gt;

&lt;p&gt;During July of 2011, I presented to the confirmation panel at Monash.
This is mandatory for PhD students at the one year mark in their research.
The goal is to present the current status of the project, and where it aims to go.
The panel then decides whether or not you should revise what you are doing, and whether you can keep your scholarship and continue researching.
Needless to say it is a little bit nerve racking, but it ended up going quite well.
I’d like to thank my supervisors &lt;a href=&quot;https://web.archive.org/web/20110412051502/http://www.infotech.monash.edu/about/staff/Grace-Rumantir&quot;&gt;Grace Rumantir&lt;/a&gt; and &lt;a href=&quot;https://web.archive.org/web/20130123085004/www.sims.monash.edu.au/staff/fb/&quot;&gt;Frada Burstein&lt;/a&gt; for their feedback on numerous revisions of my proposal, as well as the &lt;a href=&quot;https://web.archive.org/web/20200128115423/https://users.monash.edu.au/~dtaniar/&quot;&gt;confirmation panel&lt;/a&gt; for the feedback they gave me after the presentation.&lt;/p&gt;

&lt;h2 id=&quot;proposal-document&quot;&gt;Proposal Document&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;/archive/talks/phd/confirmation/proposal.pdf&quot;&gt;Download the PDF of the proposal&lt;/a&gt; for which this confirmation presentation is based.&lt;/p&gt;

&lt;h2 id=&quot;video-of-presentation&quot;&gt;Video of presentation&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://vimeo.com/26660684&quot;&gt;Vimeo Link&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;slides-used-in-presentation&quot;&gt;Slides used in Presentation&lt;/h2&gt;
&lt;p&gt;Please &lt;a href=&quot;/archive/talks/phd/confirmation/talk.svg&quot;&gt;click the image below&lt;/a&gt; to view the presentation in your browser (~2.5mb, doesn’t seem to work in Internet Explorer).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/archive/talks/phd/confirmation/talk.svg&quot;&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/phd-confirmation-thumb.png&quot; alt=&quot;Screenshot from slides showing a flowchart of a potential model to simulate scenarios&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>ISCRAM 2011: PhD colloquium</title>
   <link href="https://pserwylo.github.io//2011/05/22/iscram-2011-phd-colloquium"/>
   <updated>2011-05-22T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2011/05/22/iscram-2011-phd-colloquium</id>
   <content type="html">&lt;p class=&quot;legacy-warning&quot;&gt;If this is of interest to you, it is worth noting this is &lt;em&gt;not&lt;/em&gt; the topic of my final PhD. That pivoted quite substantially between this presentation and the final thesis in 2016.&lt;/p&gt;

&lt;p&gt;At &lt;a href=&quot;https://web.archive.org/web/20120219152309/iscram2011.lnec.pt/&quot;&gt;ISCRAM 2011&lt;/a&gt;, Tung Bui organised the PhD Colloquium which I (in addition to several other students) attended.
The talk outlining the focus of my PhD (at the time of the conference) is available below.&lt;/p&gt;

&lt;p&gt;Please &lt;a href=&quot;/archive/talks/iscram/2011/phd.svg&quot;&gt;click the image below to view the presentation&lt;/a&gt; in your browser (3mb, doesn’t seem to work in Internet Explorer).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/archive/talks/iscram/2011/phd.svg&quot;&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/iscram-phd-colloquium-thumb.png&quot; alt=&quot;screenshot from talk showing an overview of the PhD plan&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>ISCRAM 2011: Predicting patient rates</title>
   <link href="https://pserwylo.github.io//2011/05/19/iscram-2011-predicting-patient-rates"/>
   <updated>2011-05-19T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2011/05/19/iscram-2011-predicting-patient-rates</id>
   <content type="html">&lt;p&gt;Below you will find my presentation at ISCRAM 2011, entitled “Predicting Patient Presentation Rates at Mass Gatherings using Machine Learning”.
It was for an article co-authored by &lt;a href=&quot;https://www.flinders.edu.au/people/paul.arbon&quot;&gt;Paul Arbon&lt;/a&gt;, &lt;a href=&quot;https://web.archive.org/web/20110412051502/http://www.infotech.monash.edu/about/staff/Grace-Rumantir&quot;&gt;Grace Rumantir&lt;/a&gt; and I.
It was accepted in the &lt;a href=&quot;https://web.archive.org/web/20120518222708/http://iscram2011.lnec.pt/&quot;&gt;“Planning and Foresight”&lt;/a&gt; track chaired by &lt;a href=&quot;https://web.njit.edu/~turoff/&quot;&gt;Murray Turoff&lt;/a&gt;.
I like to think that the talk went pretty smoothly, and am looking forward to when they publish the videos.&lt;/p&gt;

&lt;h2 id=&quot;video-of-presentation&quot;&gt;Video of Presentation&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://vimeo.com/24708973&quot;&gt;Vimeo Link&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;slides-used-in-presentation&quot;&gt;Slides used in Presentation&lt;/h2&gt;

&lt;p&gt;Please &lt;a href=&quot;/archive/talks/iscram/2011/article.svg&quot;&gt;click the image below&lt;/a&gt; to view the presentation in your browser (~5mb, doesn’t seem to work in Internet Explorer).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/archive/talks/iscram/2011/article.svg&quot;&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/iscram-thumb.png&quot; alt=&quot;Screenshot of slides from presentation&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>OSDC 2010: Bad visualisation</title>
   <link href="https://pserwylo.github.io//2010/12/30/osdc-2010-bad-visualisation"/>
   <updated>2010-12-30T00:00:00+00:00</updated>
   <id>https://pserwylo.github.io//2010/12/30/osdc-2010-bad-visualisation</id>
   <content type="html">&lt;p&gt;I was lucky enough to be accepted to talk at OSDC 2010, which was a well organised conference of like minded Open Source developers and enthusiasts in Melbourne.
My talk was not directly related to any open source technology, but rather some general guidelines about what &lt;em&gt;not&lt;/em&gt; to do when visualising data.
It is a commentry on the works of people like &lt;a href=&quot;https://www.edwardtufte.com/tufte/&quot;&gt;Edward Tufte&lt;/a&gt; and &lt;a href=&quot;http://www.perceptualedge.com/blog/&quot;&gt;Stephen Few&lt;/a&gt;.
The talk was well received, and I was very pleased with how it went.&lt;/p&gt;

&lt;h2 id=&quot;video-of-presentation&quot;&gt;Video of Presentation&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Unavailable&lt;/strong&gt;. Unfortunately it was hosted at http://blip.tv/play/AYKQpVsC which is now defunct.&lt;/p&gt;

&lt;h2 id=&quot;slides-of-presentation&quot;&gt;Slides of Presentation&lt;/h2&gt;
&lt;p&gt;Please &lt;a href=&quot;/archive/talks/osdc/2010/pres/final.svg&quot;&gt;click the image below&lt;/a&gt; to view the presentation in your browser (3mb, doesn’t seem to work in Internet Explorer).&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/archive/talks/osdc/2010/pres/final.svg&quot;&gt;&lt;img class=&quot;border&quot; src=&quot;/assets/images/osdc-2010-thumb.png&quot; alt=&quot;Screenshot of slides from presentation&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 

</feed>
