Skip to content

Latest commit

 

History

History
691 lines (476 loc) · 59.5 KB

blog.org

File metadata and controls

691 lines (476 loc) · 59.5 KB

Pages

About

Hello! I’m Sky Leite, a 23 years old Software Engineer from Brazil. Fascinated by programming from a very young age, my first “real” project was a Discord bot used to alert users of random events in the japanese game Phantasy Star Online 2. It was a moderate hit, and the fact that it impacted the lives of so many people (including myself) was a huge influence on my career.

My goal with this blog is to share some of the knowledge I’ve gained over the years, and also improve my writing. There’s also a therapeutical aspect to it I haven’t figured out yet :P

If you wish to contact me, feel free to shoot me an email at [email protected].

Library

This page lists helpful articles I came across online. Maybe they’ll help you too :)

Posts

Topic

Understanding Asynchronous Javascript

The most common issue I’ve seen beginners face when learning Javascript is its asynchronous nature. Understandably of course, as it’s considerably different from writing fully synchronous code, which is what is usually taught first. My goal with this article is to provide a comprehensible and easily approachable explanation for newcomers to Javascript or asynchronous programming in general.

Take the following sample:

const add = (a, b) => {
    return a + b;
};

const result = add(20, 10);
return result;

In it, the interpreter does the following:

  1. Define a constant named `add` that holds an arrow function
  2. Define a constant named `result` that holds the return value of `add(20, 10)`
  3. Return the value of the constant `result`

As you already know, that means the code runs in the order it’s read, from top to bottom and left to right. This is very useful for simple programs because it keeps the code clean, concise and easy to follow, but such approach poses problems for more complex projects.

For example, say you have a file called notes.txt and you want to read and count how many notes it has. Assuming the file has one note per line, we can achieve this goal like so:

const fs = require("fs"); // Load the `fs` module.
                          // It is responsible for interacting with the filesystem

const fileLocation = "notes.txt";
const fileData = fs.readFileSync(fileLocation); // Read our file into memory
const notes = fileData.split("\n"); // Split our file by the newline character

console.log(`You have ${notes.length} notes`); // Finally, log the result

Great! This example works perfectly, but it’s not very elegant. If we’re processing a large file, this operation could take a relatively long time and confuse the end user, since the process will completely freeze until the file is read. A common way of adressing this issue is with loading spinners, but due to Javascript’s single-threaded nature that’s not possible, as it would require running code simultaneously… right?

What is asynchronous?

Asynchronous code is simply some piece of code that doesn’t necessarily run at the same time as another. This is useful in our example because it means we can run the code for our loading spinner while the file is being processed. Crazy, right? Here’s how I would do it, using the cli-spinner library for simplicity:

// Load the `fs` module.
// It is responsible for interacting with the filesystem
const fs = require("fs");
const Spinner = require("cli-spinner").Spinner;

const fileLocation = "notes.txt";
const spinner = new Spinner("Processing...");

// Define the function we want to be
// run when the file is done being read
const done = (error, fileData) => {
    if (error) {
        console.error(`ERROR: ${error}`);
        return;
    }

    // Split our file by the newline character
    const notes = fileData.split("\n");

    // Stop the spinner
    spinner.stop();

    // Finally, log the result
    console.log(`You have ${notes.length} notes`);
}

// Read the file, and pass a reference to our function
// to be run once the file is done reading
fs.readFile(fileLocation, done);

// Start our spinner
spinner.start();

Note how we don’t manipulate the data instantly. The fs.readFile function expects a reference to a function as the second parameter, which is then called when the data we need is ready. In the meantime, however, the node process is free to do whatever else it wants (in this case, show our little spinner). This is called a callback, and for the longest time it was the de facto way of doing asynchronous programming in Javascript. But such approach is not perfect.

The problem with callbacks

Expanding our example, say that in addition to displaying how many notes the user has, we also want to display how big the file is. To do so, we use the fs.stat function, and like with fs.readFile, we also need to use callbacks. Since we want to display that information after we read the file, we must register our new callback in the done function, like so:

// Define the function we want to be
// run when the file is done being read
const done = (error, fileData) => {
    if (error) {
        console.error(`ERROR: ${error}`);
        return;
    }

    // Split our file by the newline character
    const notes = fileData.split("\n");

    // Stop the spinner
    spinner.stop();

    // Finally, log the result
    console.log(`You have ${notes.length} notes`);

    fs.stat(fileLocation, (err, fileInformation) => {
        if (err) {
            console.error(`ERROR: ${err}`);
            return;
        }

        console.log(`Your file has ${fileInformation.size} bytes of information`);
    });
}

Instead of defining a second function for this, we use an inline arrow function for convenience. As you can see, this introduces a couple problems, both of which get progressively worse the more callbacks we need to chain together:

  1. One more level of nesting, making our code hard to read
  2. We need to come up with new names for our callback parameters, as the previous variables are still in scope.

We can work around these issues by making each callback it’s own top-level function, but that is cumbersome for simple operations like this. With these issues in mind, the community came up with Promises, which aim to provide more flexibility and reduce nesting when working with asynchronous Javascript.

Promises

Promises not only offer a cleaner way of chaining asynchronous operations, but by nature also allow you to do all sorts of cool things like running multiple asynchronous operations in parallel or even “racing” promises, where only the first to complete is used.

Here’s how our example looks when using promises instead of callbacks:

// Load the `fs` module.
// It is responsible for interacting with the filesystem
const fs = require("fs").promises;
const Spinner = require("cli-spinner").Spinner;

const fileLocation = "notes.txt";
const spinner = new Spinner("Processing...");

// Read the file
fs.readFile(fileLocation)
    .then((data) => {
        // Split our file by the newline character
        const notes = fileData.split("\n");

        // Finally, log the result
        console.log(`You have ${notes.length} notes`);

        // We are done with our first promise, so we can return another one
        // Since fs.stat returns a promise, we can conveniently return it
        return fs.stat(fileLocation);
    })
    .then((data) => {
        // Here `data` refers to the data returned by `fs.stat`
        console.log(`Your file has ${fileInformation.size} bytes of information`);

        // Stop our spinner
        spinner.stop();
    })
    .catch((error) => {
        console.error(`ERROR: ${error}`);
    });

spinner.start();

Even if you don’t yet understand how that works, you can see how the code looks a lot cleaner. To start using promises, you need to understand a couple of things.

A promise is an object like any other. While it can vary by implementation, you can assume every promise has at least these two methods:

  1. .then() :: Takes a function as the first argument to be run when the promise resolves (completes). Basically your way of saying “do this, then that”
  2. .catch() :: Like .then(), takes a function as the first argument to be run when the promise rejects (errors). It is important to always catch (handle) promise rejections, even if you just log them somewhere. If you don’t, you’ll get a warning in the console and in the future a crash in your application.

With that in mind, the usual workflow when working with promises is:

  1. Call a function that returns a promise (in this case, fs.readFile)
  2. Call .then() on the returned promise with a callback for what we want to do with the data
  3. If chaining, call another function that returns a promise and return it. This can be done indefinitely, of course.
  4. Call .catch() to handle whatever errors our promise chain can potentially throw.

This is the most basic overview of how asynchronous operations work in Javascript. There’s a lot more to cover, like async/await and Promise.all(), but this should be enough to get you started. If you have any questions, refer to the FAQ and feel free to post a comment if that doesn’t help or if you believe this article can be improved.

FAQ

  1. Q: Can I get data out of a callback / promise?

    A: No. Since callbacks / promises run at some indeterminate time in the future, trying to do so will lead you to all sorts of weird bugs that are hard to trace back. Usually you should treat data that’s inside a callback / function as 100% limited to that scope, that way you can avoid these problems altogether.

  2. Q: Can I wait for a promise to complete before doing something else?

    A: No. If you want to run an operation after a promise resolves, you must do it inside the callback of .then().

Station Diaries #1 - Start of Something New

With how accessible internet connections are these days, the explosion of streaming almost feels like a natural progression of the way we consume media. In the case of music, we’ve never experience so much convenience since all you have to do to listen to your favorite album is to launch Spotify, type its name and click play.

That said, this convenience comes with important and potentially dangerous pitfalls such as giving Spotify data about what you listen, when you listen and where you listen. This should be enough reason to consider an alternative if privacy is at all important to you, but if that’s not the case maybe the case for artist profits should be. Spotify pays, at maximum, US$0.0084 per stream to the holder of the music rights (which includes the record label, producers, artists, songwriters, and who knows what else). This means that 1 million streams, an impressive feat if you ask me, generates US$7,000 (which the artist might not get even half of).

With those concerns in mind I decided to start Station, a self-hosted music streaming service, in hopes of encouraging people to start buying music once again or suport their favorite artists in some other way (like going to concerts!). The idea is that you set it up once and are on your way to having your very own Spotify, running wherever you’d like. You and other users can add music to the library to be shared with eachother effortlessly, without giving up the convenience of modern streaming services.

Welcome to Station Diaries, a series of posts where I’ll detail progress on this admittedly ambitious project.

How?

I’ve been writing JavaScript for a good 3 years now and my first instinct was to use it for this project as well. It took some convincing but I decided to try .NET Core and it’s been a good (albeit rocky) journey, even if I still think it’s weird to write code in an environment where so much is abstracted away from the programmer.

Since I’m already learning an entire new language and framework, I decided to also go with a different approach with regards to databases. I have had so many terrible experiences with ORMs in the past that I couldn’t justify giving yet another one a try, which led to using stored procedures / functions for everything that deals with the database. Creating a user? SELECT * FROM createuser(email, password). It is definitely weird writing SQL as functions, especially considering there is no linting / completion / syntax checking whatsoever, but it’s honestly not much different from writing JavaScript and running your code with pretty much no confidence that it will run. I must say I didn’t miss the feeling of shock when you run code and it works, though.

What?

Some key characteristics I believe will make Station a pleasure to use and maintain are:

  1. Plugin system The application was designed from the start to work in a plugin system. By default it has no knowledge of how and where to acquire tracks, it only parses data returned from plugins. This allows users to extend the upload system with whatever sources they’d like (Soundcloud, YouTube, etc) without risking the application’s legitimacy. Station in no way wants to promote piracy, but there are completely valid reasons to acquire music from the listed sources, so a plugin system puts that responsibility on the plugin loaded by the user.
  2. MusicBrainz integration Music organization is a nightmare. There are so many edge cases that I could spend the time it takes to finish a Dream Theater album and still not be done. Because of that, Station uses the MusicBrainz database as the ultimate source of truth; if a track cannot be found on it, expect undefined behavior and dead animals. This can be a burden for a user, but it can be easily fixed by adding your entry to the MusicBrainz database, improving Station for yourself and MusicBrainz for everyone :)

As of writing this post, I have mostly figured out the song creation part which I believe to be the most crucial and sensitive part of the application. The current process of uploading a new song works as follows:

  1. `SongWorker` class receives a response from a plugin, which includes a byte array representing the music file, it’s name, artist, album, duration and, optionally, a MusicBrainz ID.
  2. Worker tries to find more information about the track on MusicBrainz. If it fails, it aborts the operation entirely.
  3. Creates Artist, Album, Song and Upload objects accordingly and returns the new Song object to the user.

The logic is simple, but it involves a lot of (admittedly ugly) code that I’m hoping to clean out later.

Currently I’m working on the authentication system using ASP.NET’s AuthenticationHandler and a system of claims. There are no permissions in place at the moment, as my current goal is to get an MVP working as soon as possible.

That’s all I got for now. Thanks for reading this far and if you’d like to contribute feel free to check Station’s repository on Github or contact me at [email protected]. Issues, PRs and comments are, as always, welcome :)

Review: Final Fantasy XIV

This is a copy-and-paste of my Steam Review for Final Fantasy XIV. I decided that it would be good to have it here as well for future reference, as it was extremely well received on the site.

I was very skeptic to play Final Fantasy XIV at first. Coming from Phantasy Star Online 2 back in 2015, FFXIV looked slow, stiff, and overall not challenging to play. The subscription model made it even harder to justify playing it, as I had always seen monthly subscriptions as a way to milk players’ money as much as possible. When I was gifted the game by a friend (there was no free trial back then), I decided to give it a try just to confirm my thoughts. I could not have been more wrong.

Disclaimer: as of writing this, I have around 500 hours of playtime on the standalone version. I’ve played through two of the first expansions and I’m halfway through Shadowbringers.

As someone who usually ignores stories in video games as they tend to not conform to my standards, I was blown away by how captivating this game’s narrative is. It’s incredibly refreshing to come across an MMO that not only has a good story, but knows it has a good story. FFXIV has no problem throwing you 10 quests in a row where you don’t fight a single enemy, because it knows the story alone is enough to keep you interested, and it is absolutely right. Every sentence was crafted to enhance your immersion in the world through character development that goes way beyond what I’ve come to expect from JRPGs, and story beats that hit you like a truck when you least expect them. If you care in any way about story-driven video games, you cannot miss this game.

The combat, at first, is slow. You’ll be pressing the same three buttons over and over for a good couple of hours when you first start this game. While I considered it a problem as an MMO veteran when first starting out, it makes sense as an introduction to non-MMO players, or even people not used to games in general. That said, the game quickly develops on your class’ toolkit giving you action after action until you have two hotbars of skills that are all universally useful in the right situations. Some of the more fast-paced classes (Ninja, Astrologian) feel like playing Dance Dance Revolution as a kid, all while dodging whatever the gigantic f*****g monster you’re fighting throws at you. I’ve not yet done end-game raid content, but from what I’ve seen of my guild mates they expand on this even further.

If you don’t play this game, it’s completely understandable to be flustered by the payment model. Paying for a game, expansions and a monthly subscription on top of that rings a siren in the ears of any sane consumer, but this is different. Final Fantasy XIV does NOT pull any tricks to keep you subscribed. You get a good game with a huge amount of content and that’s it. If you play for the story, you can subscribe while there’s story content and simply stop paying until the next patch. If you play for raids and end-game content, you can pause your sub once you’re tired or feel like playing other games.

Three years ago, a japanese player asked YoshiP (the game’s director, Naoki Yoshida) how to stay motivated to keep playing in between patches, when content naturally dries out for a little while. His response was the following:

“It’s alright not to play it everyday. Since it’s just a game, you can stop forcing yourself if it’s hard on you to keep that up. Rather, it’ll just pile up unnecessary stress if you limit yourself into playing just that one game since there are so many other games out there. So, do come back and play it to your heart’s content when the major patch kicks in, then stop it to play other games before you got burnt out, and then come back for another major patch. This will actually make me happier, and in the end, I think this is the best solution I can answer for keeping your motivation up for the game.” (source: /u/elevenmile on /r/ffxiv)

Reading this response is what made me realize what kind of experience I was paying for. In PSO2, to play end game content you have to log in at certain times of the day where such content is available, otherwise you miss out completely. After playing for years I grew resentful of the game because it felt more like a job than an enjoyment for me, yet I couldn’t quit because I’d be behind if I did. In Final Fantasy XIV, you’re experiencing the world on your own terms. No pressure, no time gated content, no manipulative tricks to keep you subscribed. If you don’t feel like playing, just don’t. The developers very clearly respect the player’s time and it shows in game.

I can’t force you to play this game, but I hope my thoughts helped you decide for yourself. Even if you ultimately decide to not play it, I recommend you watch Noclip’s FINAL FANTASY XIV Documentary on YouTube. The development cycle of FFXIV and how it went from a financial disaster to SquareEnix’s biggest hit is a great story.

See you in Eorzea, Warrior of Light.

Manipulation.app

I have this insatiable desire to create, and it is ultimately what led me to start my programming career with Weeb Bot back in 2016. However, after finishing it I could never bring myself to lead another project to completion. Along with the normal impostor syndrome that comes with working in a field filled with so many incredibly talented people, it led me to develop anxiety over starting new projects and bringing ideas to life. After going through therapy, I’m glad to announce I’m working on yet another project called Manipulation.app, a web application designed to guide you through your crafting experience in Final Fantasy XIV.

The problem

In Final Fantasy XIV, one of the best ways of making Gil (in-game money) is through crafting items (food, gear, potions, etc.) through the many different crafting classes and recipes. Each recipe has a certain amount of ingredients that can be either gatherable or craftable items. Once you reach end-game recipes, this can lead to items that require many other craftable items, creating this seemingly un-ending web of gathering and crafting that the game makes no effort of untangling. This is where Manipulation comes in.

Through a feature called “Lists”, the user can input an array of items they’d like to craft, and the application creates a list of all the items they’ll need to gather and craft to reach the desired outcome, along with their requirements.

The existing solutions

Tools designed to solve this problem have existed for a while in the community, but each of them come with their own myriad of problems. Garland Tools is an incredible database of general information for the game, but doesn’t help you to streamline the crafting process that much. FFXIV Teamcraft is much better in that respect, with many other helpful tools, but suffers from significant performance problems, as on every load it downloads every single item, quest, recipe and NPC in the game. This results in an initial load of 11.69MBs of data and a time-to-interactive of 3.11s (which you still have to wait after, for some reason). This is unacceptable.

https://i.imgur.com/LkgpGnX.jpg

All the data is stored server-side in Manipulation, and through GraphQL it allows the client to pull precisely the data it needs, not wasting a single byte. This improves initial load times, general CPU-time performance (as the browser doesn’t have to parse 10MBs of json data) and general responsiveness, especially on mobile (which Manipulation is designed to be fully compatible with). All these improvements come with a higher network footprint, as the application needs to always be connected to the internet to work, but considering Final Fantasy XIV is an online-only I don’t consider this to be a problem yet.

The technology

Manipulation has a few moving parts that all come together to deliver you the best user experience possible, and in this section I’ll talk about them briefly and explain some of the decisions I made along the way.

Datasync

I consider this to be the most crucial part of the entire application. It’s purpose is to download data from the FFXIV Datamining Repository, parse the (weirdly formatted) CSV files and convert the data to the format used in Manipulation’s database. I chose to write Datasync in Rust, as the CSV files are huge and Rust’s CSV parser is incredibly fast. Considering the synchronization process will not run more than once a day the speed doesn’t matter too much, but it helps a little during development.

Backend

The brain of the application uses Elixir and leverages the fantastic Phoenix Framework. I have lots of reasons for choosing Elixir for this project, but the most significant one is being able to make use of the BEAM, which is a virtual machine that’s part of the Erlang ecosystem. If you don’t know what that means, it basically makes any program written in this ecosystem incredibly easy to scale, and letting me skip having to learn Kubernetes just yet.

Another important part of the backend is GraphQL. It allows the frontend to request only the data it needs, making network requests significantly less costly and development time much faster. All the backend needs to do is define the schema of the data, and the frontend requests what it needs. No need to figure out everything from the start through REST endpoints!

Frontend

I love Elm. Developing for the Browser is a much more pleasant experience when instead of writing with no types, you write all your business logic in types first and implementation second. And of course, it all ties together nicely with the fantastic elm-graphql package by Dillon Kearns. What this package does is use the GraphQL introspection queries to generate a type-safe interface to your API, which gets checked by the compiler just like any other module.

The release

As of right now, Manipulation.app is not yet released. I’m working on it full-time, and expect an open beta to be completed in a month or so. In the meantime, you can follow the development on the Github repository or my account @[email protected], and feel free to shoot me an e-mail at [email protected].

Persona 4

I’m not a fan of animated series. Specifically, I’ve always had a really hard time relating to japanese animation because more often than not the characters’ experiences do not mimic my own in high school at all. This causes an uncomfortable disconnect, where I’m intrigued by the extremely compelling and out of the norm premises but cannot relate to any of the main characters. Of course, in such an old medium there are bound to be series that portray experiences I can relate to, and one of them is Kaiji: Ultimate Survivor – the life of a young adult who finds himself in crippling debt and has to put his life on the line on a sadistic game to be get out of it.

You must be asking yourself what any of this has to do with Persona, which is a fair question. At the time of writing I’m a relatively successful person at the age of 23, and could not be further from the reality that Persona 4 expressses, and that was also true in my teenage years. I wasn’t good in school, I didn’t have tens of social links friends and I certainly didn’t have a TV I could jump into to escape reality. My teenage years were fairly run-of-the-mill, and I believe that’s the reason I hated my every minute with the Persona series back then. As an impressionable teenager, seeing such an interesting and diverse day-to-day on the small screen of my PSP made me feel like that was the life I was supposed to be living, not the boring and uneventful one I was stuck with. And now, as a fully formed adult, that’s not the case anymore.

The Setting

Persona 4 starts with you, the main character, moving from the city to a small town in countryside Japan to live with your uncle Mr. Dojima, a detective at the local police station, and his young daughter Nanako. The game quickly turns into a murder mystery when a local journalist is found dead after being outer as a famous man’s mistress. The town of Inaba sets the mood perfectly to this premise, with its quiet nights, rainy days and huge empty lots by the roadside. This is a setting you’re familiar with if you’ve ever interacted with the When They Cry series, which predates Persona 4 by 6 years.

Another interesting view on Persona 4’s setting is that it doesn’t need Persona 3’s on-the-nose themes of death and darkness to be frightening or uncomfortable. Here most of the tense action happens in either broad daylight or the cheery and colorful TV world (more on that later), almost making you scared of the broad daylight. If you’ve ever seen The Texas Chainsaw Massacre you’re more than familiar with this dichotomy, where not even the sun can save you and your friends, and it’s incredibly refreshing to see a take on this concept from a video game.

In contrary to most video games featuring co-protagonists, in Persona 4 your friends are extremely important in every single aspect of your experience. After all, you’re still a high schooler and you have no one you can relate to besides the other teenagers who are going through the same hellish years you are. Just like in real life, you need friends to survive not only the hardships life throws at you, but also the ones you create in your own head through trauma, stress, anger and anxiety. Where usually a video game would emphasize the importance of your friends through an instant game over screen when they die in combat, Persona 4 integrates it into the narrative through Social Links. Spending time with your friends makes you closer, increasing the knowledge you have of each other and the power you gain from extending your support network through (meaningful) stat bonuses when creating Personas compatible with each Social Link.

On short media

I used to love anime as a kid. In 5th grade I was introduced to Naruto by a friend through home-burned DVDs filled with .rmvb video files that were so compressed each episode wouldn’t take more than 20MB of space. It was a weird experience initially, since I had no familiarity with japanese culture or watching subtitled media in general, but it quickly grew on me and I caught myself watching dozens of episodes every day after school. Years later, now at the age of 23, I found myself completely apathetic towards anime as it’s been years since I watched a series fully, but recently my passion was re-ignited through a rewatch of Kaiji: Ultimate Survivor with my girlfriend. It led to me watching the first season of Kakegurui and a few episodes of Hunter x Hunter, a series I followed the first few episodes on release a decade ago, in a single sitting.

Just as when I was an 11 year old watching Naruto for the first time, it was a weird experience. I had not watched anime in years, so why now, when I’ve never been busier in my entire life? It took some thinking, but I reached a pretty jarring conclusion, and it has to do with the shorter length of each episode. Compared to TV shows, where the average runtime of each episode is 45 minutes, anime episodes never pass the mark of 25. This means that directors have a lot less time to pad, thus episodes have to be more to the point than TV shows. You’d think this would be an objective detriment to the media, as less time means less development of characters, scenery, etc, but anime specifically finds incredibly smart ways of contextualizing their subjects without exposition, so it loses nothing for it.

Another interesting discovery was that this extends beyond anime as well. Last week Resident Evil Village came out, and it led me to trust this theory even more with its ~9 hour runtime. Throughout the game, the protagonist Ethan is characterized not through lengthy cutscenes (which obviously have their own merit as well), but through mid-combat banter, short cutscenes filled with charismatic people and his hands. Something as simple as Ethan’s hands is used to show that unlike other protagonists in the series he’s not a trained super-soldier, but a mere civilian thrown into a nightmare through the way he handles firearms and reacts to injuries. The latter is something that’s become a meme recently, and is beautifully written into Resident Evil canon by the end of the game. In it’s comparatively short runtime, RE8 manages to accomplish all this along with great combat, meticulous pacing, intriguing plot, and faithful homages to other historic horror franchises (even Resident Evil itself), making it a prime example of how media being short isn’t necessarily a detriment to its quality.

If you’re one of the people who immediately jump to the conclusion that a game’s short run time is a bad thing, I understand but also ask you to reconsider. You might find yourself having way more fun in a 10 hour game than in a 60 hour game. And you might even find yourself replaying it (like Resident Evil games are designed to), increasing your dollar-per-hour value.

Lessons I learned from getting infected by a crypto miner

It’s always so funny to me when someone gets pwned. I love it when my peers tell me stories of their family members getting phished out of their Facebook or Roblox accounts, mostly because it reinforces the belief that it could never happen to me. Surely I’m smart enough to not click a random link in an e-mail from [email protected]. But if you read the title of this article you already know where that led me.

Satisfactory

As every good story, this one starts with a terrible video game. Satisfactory is a game about building factories (think Factorio in 3D), which is a great pitch for a multiplayer game, and fortunately Satisfactory has full multiplayer support for up to 128 players. Except, of course, for the minor fact that it only works 5% of the time. Because of that, my friend and I simply cannot connect to each other for seemingly no reason, even though we can connect to other people and play (even to the same person!).

This led me to a seemingly infinite rabbit hole of networking, a subject I’m terrible at, trying to figure out why in hell we cannot build a factory together. I forwarded ports, messed with the firewall, disabled NAT (do not do this) and even enabled DMZ for my machine (DO NOT DO THIS!!!) effectively stripping my network of any modicum of security it previously had. Unfortunately my efforts were useless, so I decided to do something better with my life than wrestle with video games that do not want to be played.

The next day

I run a few Docker containers for development at work, mainly Redis and Postgres. Because the application itself is not run in Docker, I have Postgres’ ports expose to the host. And because this is only for development, I never bothered to change the default credentials.

After I was done with work, I decided to play some Factorio, a game I haven’t played in a while. Since it’s also about building factories but was made by moderately competent engineers, it served as a nice break from Satisfactory. For some reason, my machine was running unusually slow and I decided to investigate. Running htop left me flabbergasted: a single procress I had never heard of, /tmp/kdevtmpfsi/, was pinning all my CPU cores to 100%.

“Surely this is just some KDE dependency or something. Let me google that name real quick” - A big idiot

The retribution

It was a crypto miner. “How could this happen?”, I thought to myself. It didn’t take long to figure out what happened, since apparently there is a sea of unsuspecting idiots like myself who ran into the exact same problem. So here’s all the information I got, condensed in a way that’s easily digestible.

  • Assume everything in your Docker Compose is vulnerable

It doesn’t matter how prestigious of a name your database manager has. It doesn’t matter if it was made by Facebook, Google or the fucking Pope. As I just learned, even a project as big as Postgres (or Redis) can be vulnerable to Remote Code Execution.

  • Do not use default credentials (yes, even for development)

It will come back to bite you. All it takes is one slip up, and you’ll be exposed.

  • Tag your images to major versions

For example, if your image is tagged to postgres:12-alpine and a new security patch gets released, it will be updated. If you are targeting a minor version, such as postgres:12.4-alpine, security patches will not be applied should your containers be redeployed.

  • Docker does not care about your firewall

It has been brought to my attention that, for some reason, Docker completely bypasses UFW. I’m not entirely sure if it applies to other firewalls as well, but to be completely safe, assume it applies to all firewalls and enable whichever one you have on your router.

  • For the love of fuck, don’t expose your machine to the internet

The world waiting for you at the other end of the Ethernet cable does not care about your friday night plans.

Conclusion

Someone in China is probably a few dollars richer now, and all I got in return was more awareness about security and online responsibility. At least all it cost me was a few CPU cores.

Review: Danganronpa V3: Killing Harmony

This review contains spoilers for the entire Danganronpa series, as well as the TV show Twin Peaks.

Twin Peaks is a TV show that began airing in 1990, written by David Lynch and Mark Frost, about the murder of a young girl in a small town called Twin Peaks. It follows the story of FBI detective Dale Cooper as he attempts to unravel the mystery of who killed Laura Palmer by learning about her relationship with others in Twin Peaks and how they were affected by her death. Its uncanny style of storytelling, melodramatic characters and surreal humor led the show to acquire a sort of cult following some time after its release, not unlike Danganronpa.

The show never explicitly tells the viewer, but through clues scattered throughout hundreds of hours of material (including interviews with David Lynch himself) allow the sharp minded to deduce that Twin Peaks was never really about who killed Laura Palmer, unlike every other show airing at the time, but instead about the town, the people who lived there, and how the death of a young girl impacts them. Lynch very explicitly says in an interview that Laura’s murderer was never supposed to be revealed, and only was so due to pressure from viewers who wanted “closure” and ABC, the company that broadcast the show. 1990s TV was infected with “murder of the week” shows, where every week a character would be created solely to suffer and die, and for the audience to be quickly rewarded with the reveal of who killed them. Lynch believed this phenomenon was undesirable, and thus Twin Peaks was created to balance it out, with a murder that was never supposed to be solved.

Like every other murder mystery in the past 20 years, Danganronpa V3 is not afraid to show how it was influenced by Twin Peaks, especially after the ending. It is revealed to the cast that they’re characters in a TV show called “Danganronpa”, and the only reason for them to exist is to die for the pleasure of the audience, and of course this extends to the characters in the other 2 games as well. In the midst of this reveal is a bunch of jabs at the audience as if they’re just entitled crybabies who would have the game going forever, as seeing other people suffering is all that matters to them, with the main character at some point literally battling the audience for the right to end the killing game.

In the moment this works very well. It comes completely out of left field and the constant breaking of the 4th wall keeps the audience engaged through the clusterfuck that’s unraveling before their eyes. But at the same time, it feels incredibly self indulgent for a studio to make three entire games where high schoolers are forced to kill each other, just to at the very end throw a punch at the audience as if it’s wrong to enjoy this kind of entertainment. I don’t have an issue with this belief, and is certainly a position I can see being defended, but I don’t think it makes sense for it to be defended by the people who used this very tactic to make bucketloads of money off of this very premise. From my perspective, watching people kill each other for fun is certainly less reprehensible than making them do so in the first place.

This is a position similar to the one that led Twin Peaks to exist, but it seems to miss the entire point of the show it is taking influence from. Lynch doesn’t think fictional murder is bad, or that audiences are depraved for enjoying that kind of content, but rather that murder doesn’t always have to be the entire point. It’s a much less judgemental and subtle commentary on the subject, and it certainly didn’t need a literal fucking boss fight with a stand-in for the audience to deliver that message.

Miu is the best character ever written though. 10/10

Porting a mod of a mod of Doom to WebAssembly

2023/07/09 EDIT: Fuck it I ported the other game too LOL. Play Sonic Robo Blast 2 here

You read the title correctly. This is a blog post about how I ported a mod of a mod of Doom to WebAssembly, essentially allowing it to run in any device with a web browser from the past decade.

You’ve probably come across videos and stories of Doom, the 1980s video game, running on various devices like an ATM, an iPod Nano, or even a pregnancy test. There’s clearly something that nerds find appealing about running video games on unexpected devices, and I am, for better or worse, included in that group.

I’ve never been too familiar with low level programming or the C family of languages, so this seemed like magic to me. Not just the fact that these devices are capable of running Doom at all (to varying degrees of stretching of the word “run”), but also that someone, somewhere, was interested enough to actually take a project like this to completion, for no reason other than the fact that it would be funny. And yet, here I am, staring at my web browser running code that was written before I was even born, just to show me Sonic the Hedgehog racing Erika Furudo from Umineko in Rainbow Road.

If you have no idea what I’m talking about, let me explain. The game in question is called Sonic Robo Blast 2 Kart, a mod of the popular fan-made game Sonic Robo Blast 2, which itself is a mod of Doom based on the Doom Legacy engine. Doom came out in December 10th 1993, Sonic Robo Blast 2 came out in February 1998 (when I was 1 year old) and Sonic Robo Blast 2 Kart came out in 2018. The latter two games are still being worked on to this day, which is par for the course for the endearingly deranged Sonic community.

I’ve been playing Sonic Robo Blast 2 Kart (from now on abbreviated as SRB2Kart) on and off since release, with not much interest due to the game’s delay-based netcode, which essentially means it sucks to play with anyone who’s physically far away from you. This changed some time ago when I discovered the brazilian community for the game, which runs regular tournaments, 24/7 servers filled to the brim with brazilian memes (like the Fiat Uno with a ladder on top, fabled to be the fastest car in existance). It was then that I learned how much fun SRB2Kart could be, and began playing more often, which led me to consider bringing it up as a potential game for my team at work to play during our Social hours. Unfortunately, the requirement to download anything is a huge barrier for playing anything ad-hoc, so I held off on it for a while, until one day I realized there was a chance this game could run perfectly on the browser. I mean, QuakeJS did it so why can’t we?

On July 3rd, 2023 I decided to consider doing it myself, and sent Tyron a message on Discord to ask if someone had already done this work. The following excerpt sums it up pretty well:

https://i.ibb.co/2hhP82g/image.png

Nonetheless, I decided to tread forward.

The Nix arc

If you know anything about me, it’s that I love Nix. With all of its flaws, doing things declaractively and functionally just makes sense to my autistic brain, so I gravitate towards tools that let me work in these paradigms. Naturally, my first instinct was to take advantage of the existing SRB2Kart package on Nixpkgs, and simply override the things I needed to compile it locally. That worked, of course, but only because I had no idea how much the game’s source code would have to change to make this even possible.

The next step would be to find out how to use WebAssembly at all. I came across a compiler toolchain called Emscripten, which is supposed to be a drop-in replacement for traditional C/C++ compilers like GCC and Clang, allowing you to target WebAssembly. Since it was also already packaged in Nixpkgs, with even a helpful function that overrides the compiler for you in an existing package, I figured it would be a piece of cake. As you can tell from my tone, it wasn’t.

First of all, none of the dependencies worked. This is obvious in hindsight, but it turns out you can’t just use good old SDL2 (or other C libraries for that matter) with Emscripten. They need to be specifically ported to with with it, so I had to figure that out. Thankfully the tool provides ready-made ports for some popular libraries, which luckily matched 1:1 with the libraries SRB2Kart needs, so it was just a matter of setting a few compiler flags.

The catch, however, is that emcc (the Emscripten compiler) grabs those ports at compile time from the internet, and Nix doesn’t allow internet access during the build phase to prevent exactly this kind of behavior and keep builds pure and predictable. emcc has a flag called EMCC_LOCAL_PORTS which, in theory, is supposed to let you specify a local directory containing these ports so it doesn’t have to download the off the internet. After an hour or so of banging my head against the wall, I came across this 4 year old issue on Github which states that it’s only possible to use EMCC_LOCAL_PORTS for SDL2, which is only 1/3 of the libraries I need to build the project. Instead of submitting a Pull Request to Emscripten like a good citizen, I decided to scrap Nix entirely for the moment and use a good old shell script.

The Last Time I Did Any Of This I Was In University

Now that emcc seemed to be working, including the acquisition of library ports, I had to face the decrepit elephant in the room: C. I’ve been a Software Engineer for over half a decade at this point, so you’d think using the language that everyone learns in university wouldn’t be a problem. I imagine that’s usually the case, but there are a few caveats:

  1. I dropped out of university after 4 months (three times, actually, but that’s a story for another day)
  2. This is a project dating back to the 1980s
  3. The source code includes ports for every device imaginable, including the Nintendo DS, the original Xbox, the Dreamcast, and even MS-DOS if you can believe it

Much to my surprise, C turned out to be rather polite for a language older than I am, save for a few compiler warnings. Most of my time was actually spent learning and wrangling CMake, the build system (or, if you’re a pedantic nerd, the build system generator) used by the project. Since it targets so many different platoforms, the CMake code was littered with IF branches to figure out what it was even supposed to build in the first place. Once I realized that I could just ignore branches dealing with Windows or the Samsung FamilyHub Smart Fridge, I eventually landed in src/sdl/CMakeLists.txt, which is the file that describes how to build the SDL2 target. Bingo.

The project has a few CMake modules whose sole purpose is to figure out where the required libraries like SDL2 and ZLIB are, and my inexperience with the tool led me to believe they were important. I spent a good few hours trying to figure out how to appease them, only to later figure out that they were getting in my way for no reason, as emcc handled such dependencies instead by manually linking to its own ports. That turned out to be a complete waste of time, but if I’m ever in a Saw situation where I have to compile Doom from source to save my life I have a decent shot at surviving.

Eventually, after much trial and error, the build worked seemingly out of nowhere and I was left with 2 files: srb2kart.js and srb2kart.wasm. Reading the Emscripten docs I found out that I had the option to tell the compiler to also generate an HTML file to have something of a building block. Once that was done (by hardcoding the output name to srb2kart.html) I was left staring at a quite fancy web page, with a black square clearly meant for the game to show up, and… nothing. When I opened the browser console I could see that the game was attempting to initialize since it was at least printing to stdout, but it seemed to be looking for… assets? And then I remembered that in the srb2kart package there is code for downloading a file called AssetsLinuxOnly.zip. I manually downloaded it, instructed Emscripten to embed the files in it to the executable, compiled… And it worked! When I saw the splash screen I was immediately filled with the feeling of accomplishment. I wasn’t there but I bet this is how Neil Armstrong felt when his feet touched the moon, or when Hatsune Miku released the first version of Minecraft.

(Somewhat) familiar territory

After the game initially compiled, and I was done picking up my jaw from the floor, I began actually playing, and was surprised to find out how much worked out of the box. Audio worked. Controller input worked. Split-screen worked. I could even play on my iPad using the Magic Keyboard. In that moment I felt like a citizen of the perfect timeline, where systems interacted perfectly, and every device shared APIs making every piece of software completely cross-platform. The web can be truly magical when things work, huh.

And then, of course, like the web developer that I am, I immediately installed a Javascript framework and Webpack. It was actually Elm, which is a programming language and not a framework, but I’m not one to let facts get in the way of humor. Anyways, from here on it was sort of smooth sailing. Using Emscripten I could call C functions directly from JS, even if in a limited fashion since I can only pass primitive values and not structs or arrays. This opens the flood gates for any kind of integration I can think of, with the amount of fucks given being the only restricting factor to something actually existing.

Over the next couple of days, I implemented a way to add Addons to the game straight from your computer with a simple button press, fullscreen support and a handy Help menu. The experience is quite decent already if I do say so myself!

The Rest of The Fucking Owl

So… Now what? I do have quite a few plans for this, actually! Here’s a list in no particular order:

  • Netplay (this will be absurdly complex since SRB2 uses UDP for connections, which is not supported in browsers. I will have to either settle for web-to-web multiplayer, or add support for something like WebRTC to SRB2Kart v1.7 [presuming the maintainers would even agree to that!])
  • Improved management of addons, like adding and removing in bulk, persisting addons through sessions, etc.
  • Download addons from the SRB2 Message Board directly to a running instance of SRB2Kart
  • Adjust client settings from JavaScript, which would allow persisting such settings between sessions
  • Improved presentation. The website looks and feels somewhat janky at the moment
  • Host assets on Cloudflare to improve load times
  • Disable in-game menus that don’t work like Multiplayer
  • Download your own replays
  • Share lists of addons with friends using simple URLs
  • Direct links to races with pre-defined addons, so modders can have something like a “Try in SRB2Kart Web” button in their threads
  • Do everything all over again for (non-kart) Sonic Robo Blast 2 so everyone can bask in the glory of SRB2Infinity

If this was interesting to you, feel free to head to https://skyleite.github.io/Kart-Public-WASM/ and try it out for yourself. Please forgive any jank you might encounter. I would be glad to receive any piece of feedback through the comments, e-mail, or the project’s Github repository.

Review: Steins;Gate

(Cross-posted from Steam)

This is probably the 6th time I’ve tried writing about Steins;Gate. It’s the most conflicted I’ve ever felt about a game, which has led me to writing and deleting multiple reviews, which culminated in another try last night when the Steam app on my iPhone crashed as I finished writing the sixth paragraph.

The reason Steins;Gate is such a difficult game to talk about is because it has soaring highs and literal nauseating lows. So many other games are like this, but Steins;Gate is on another level. I’ve been agonizing over this ever since I finished reading the novel back in May 2022, and it’s high time I put my thoughts in order with a review. I will not be going over the plot of the game, or whether you should buy it or not. As you can see, this review is marked as a negative but that’s only because Steam does not allow a neutral review. If you’re looking for a tl;dr, I enjoyed (some) of my time with Steins;Gate and I don’t regret reading it. I did multiple times during the play through.

Steins;Gate’s biggest flaw for me is something that might be its best aspect for other people. It’s the fact that it’s a story that cannot make up its mind on what it wants to be. It wants to be sci-fi, mystery, romance and slice of life all in a single 60 hour package, and ultimately fails at being either of them successfully. This is extremely frustrating because every now and then you catch a glimpse of what the game could be if it committed to being a Sci-Fi or a mystery novel, only for the whole thing to be undermined by the author’s relentless insistence on pointless teenage romance.

When Steins;Gate (briefly) commits to being a Sci-Fi however, it is easily one of the best stories I’ve read. The tension that this game builds up with nothing but music, simple visuals and voice acting is breath-taking, which managed to create very real memories in me. I’ll never forget the rainy night where I was reading Steins;Gate in my living room TV with my girlfriend laying in my lap, and this is something I’ll forever be grateful for. In these moments it really feels like the world is ending, and if the main character doesn’t find the stupid IBM computer everything will be over.

Unfortunately these moments don’t last long, or even long enough for me to call this a Sci-Fi game. Steins;Gate, for some unfathomable reason, decides to spend most of its 60 hour runtime building up unrelatable and annoying characters. Of course character development is an important part of every novel, but it’s really hard to care about any of them when the main character’s favorite past time is sexual harassment. Like everyone who’s ever lived I was a teenager once so Kyouma’s insecurities were actually something I could relate to, and even the ways in which it manifested through his general dorkyness and awkwardness (like speaking random English catchphrases, which was something I also did back in my days). The problem is the author seems to have a hard time distinguishing between a teenager being awkward and a teenager sexually harassing his female “friends”. This isn’t necessarily a bad thing, after all characters can and often are terrible people, but Steins;Gate spends so long doing this that at a certain point it stops feeling like a character being built as an asshole and becomes just the author exposing his fetishes unto you through a cast of teenagers. It’s disgusting.

I could let this go though. I could be comfortable with a 60 hour runtime that comprises of 5 hours of Sci-Fi and genuinely interesting mystery. Hell, some of my favorite novels have a similar quality curve. The problem is, at the very end, the game throws all of it away. Kyouma’s and his friends’ quest to save the world suddenly becomes Kyouma’s personal quest to save the female cast who he wants to sleep with. Of course I’m not opposed to romance, and even think there are some adorable pairs in this game, but the order of events is completely backwards. The tension is built up to literally the entire planet being in danger, only for the story to undermine itself and make it about a single person. If this happened in the opposite order I would’ve been able to care about the couple in question, and leave satisfied with a story about love and friendship triumphing inhuman odds through science fiction. Unfortunately, what I got is an anime version of The Butterfly Effect starring Ashton Kutcher (great movie by the way).

I want to note however, I really really like Makise Kurisu. She’s obviously treated like shit by the male cast, but the character itself and her arc feels very respectful as a woman in STEM. She’s strong, intelligent, and (sometimes) stands up for what she believes even if it means telling the asshole in power that he’s wrong. Every time she was on screen (and not being sexually harassed) I couldn’t wait to see what would happen. I just wish she was in a proper science fiction novel.

I want to reiterate that this is only my experience with Steins;Gate, and my reasoning for disliking it is mostly because it fails to deliver on the aspects that led me to reading it in the first place. As a general anime novel, I would say it’s probably on the better side. If that sounds interesting, don’t let me stop you.

Footnotes