building a cheaper kubernetes cluster at home

Before we get into it, I'll keep this short and sweet.

I am looking for work.

I'm a software developer specializing in backend development, infrastructure, and devops. I'm also interested in and have explored embedded firmware, rendering, and electrical engineering. Three things I really care about are reproducibility, documentation, and community. Take a look at my resume if you're hiring - or even if you're not! Heck, send it to your friends.

contact me: dwbrite at gmail dot com


At the end of 2022 I was hosting my website on Linode Kubernetes Engine. My monthly spend reached $48/mo for a slightly excessive setup - but, in fairness, I was doing a lot of experimentation on Linode.

I had just lost my job at Remediant[0] and this monthly subscription was enough to make me a little uncomfortable while unemployed, so I set a new goal:

Build my own kubernetes cluster that's stronger and cheaper, so that I could still run my website (and various other servers), should finding a job take too long.

step 1: hardware

The Orange Pi 5 came out less than two months before I decided to build my own cluster. It's an 8 core little beast with 8GB RAM for $90 MSRP, at a time when the Raspberry Pi 4B was going for $200, if you could even find it.

On the surface, OS support seemed pretty good. It was an obvious choice, if a bit overkill - being 3-4x stronger than the RPi. I bought 3 with an estimated ROI period of just under a year - not including the benefits of far more capable hardware than I could find from VPS providers.

step 2: networking

At my apartment in NYC I was behind CGNAT[1] with NYC Mesh, and IPv6 support was (is) nonexistent. That left me with two options:

  1. Giving a third party unencrypted access to all traffic flowing into and out of my cluster (see: Cloudflare Tunnels), or
  2. Hosting at my mom's house.

I opted for the latter (thanks mom), which meant progress slowed significantly until I moved back in with her.

When I got there, I upgraded our firewall/router to a mini PC running VyOS. This allowed me to define my firewall's configuration as code, upload it with Ansible, and not have to manually dig around in some UI for each change. It's similar to Ubiquiti's EdgeOS and Juniper's Junos OS in that way.

I find it incredibly comforting that my network configuration is easy to reproduce or roll back.

step 3: building the cluster

Before I could think about Kubernetes, I needed an OS to run it on. And before that, I needed to be able to boot from the NVMe drive, which the Orange Pi 5 does not support out of the box. Fortunately the process to boot from NVMe is tolerable enough - just load up an official Orange Pi distro and update the SPI_FLASH bootloader via orangepi-config.

Once I did that, I installed RebornOS on a USB and wrote a makefile to do some initial config and copy the install to each machine's NVMe drive. I chose RebornOS because it appeared to be better supported than other distributions. And honestly, the official Orange Pi distros seemed kind of sketchy[2].

I opted for k0s as my kubernetes distribution, because it's ridiculously easy to install and it allows me to declaritively define my cluster's topology.

I was also already familiar with Mirantis because of Lens, which certainly helped.

Small hiccup: while k0s does support ARM64, the k0s controller can't run on ARM because of some issue with etcd. I didn't look much into it, but...:

Poor man's solution: run the k0s controller on my x86 firewall, and remember to never ever ever (ever) open port 6443.

It took less than an hour to get a proper kubernetes cluster running.

step 4: system applications

Having worked with kubernetes only in the context of cloud providers, I expected many things to just work out of the box.

To test the cluster I attempted to deploy a basic web server with nginx-ingress-controller and cert-manager.

I found out pretty quickly that if I wanted to create a LoadBalancer Service, I'd need a load balancer. On a whim, I installed MetalLB and it worked with minimal configuration. Just as well, I now had a discrete pool of IP addresses I could port forward to.

Then I decided, fuck it, let's learn Istio, and I replaced nginx-ingress-controller. The switch was surprisingly easy, and I'd say it feels slightly cleaner overall.

I also installed ArgoCD so I could manage the applications running on my cluster without having to rely strictly on helm. This has the added benefit that I don't have to worry as much about my deployments, especially when resources deploy out of order.

step 5: continuous integration and pain

After I demo'd Istio and friends, I wanted to get my real website up. To do that, I needed to build ARM container images for it.

On Github Actions, this took over an hour. But I'm impatient, and I had some very capable little ARM machines within walking distance.

So, I deployed the github actions runner controller, to control deployment of github actions self-hosted runners. :^)

This was the first time anything on my cluster needed wanted to create persistent volumes, and apparently I did not have a storage provisioner.

*sigh*

So, I installed Ceph+Rook.

My memory of that ordeal is entirely gone, but suffice to say something didn't work, and configuration was a bit painful. Then reddit said Longhorn should be simpler, so I pivoted to that.

I installed it with ArgoCD and helm, and...

*Bzzt* šŸ¤–
You don't have iSCSI support! I can't work with this!

Turns out RebornOS for the Orange Pi 5 doesn't have iSCSI kernel modules.

But that's fine I guess. It had been several months since I installed RebornOS, and Joshua Riek's ubuntu-rockchip distro was really picking up steam. I installed ubuntu-rockchip on one machine and gave Longhorn another go. I set every nodeSelector I could find in Helm to target that machine, but alas-

*Bzzt* šŸ¤–
No iSCSI support on non-storage machines, idiot.

P.S., we don't have a way to set nodeSelector for this specific DaemonSet lol. Try taints and tolerations, I promise that'll work *wink wink*. [3]

So I set some taints and tolerations, even though I would have really preferred to stick with nodeSelectors.

*Bzzt* šŸ¤–
Are you fucking stupid?! I can't deploy this pod to non-storage machines, it's tainted!

... šŸ˜


So I started from scratch with OpenEBS. Mayastor seemed to be the best storage engine for OpenEBS, and it looked easy to configure, and it's written in Rust. So it had to be a good choice.

One small problem: it doesn't run on ARM.

...Unless?

Xinliang Liu - my hero - added ARM support to mayastor-extensions and published his images.

With a bit of modification, it fucking. worked!

Well... Almost.

There was just one more issue. Mayastor relies on nvme_fabric, which is not enabled by default in the linux-rockchip kernel.

So I enabled it, compiled ubuntu-rockchip myself, and finally got persistent volumes working. If you're looking to reproduce this, you can compile ubuntu-rockchip yourself, or use the image I built.

Funny story though, you can actually disable the volume claims on ARC, so none of this was really necessary at the time. But, once I start running my plex ser--

šŸ¤– On ARM? Haaaahahahahahahaaha

Alright then, once I start running Jellyfin on the cluster, and Outline, I'll be happy I did all that.

step 6: blue/green deployments with istio and argocd

Back to happy boring land, I created two ArgoCD Applications which point to blue and green Kustomize overlays for my website's Deployment.

Each overlay points to a specific version/tag[4] of my website's container image, and labels the resources. All I need to do to switch which one is live is modify the VirtualService and push my changes.

It's downright pleasant.

now and onwards, and other random thoughts

As painful as some of this was, I'm very happy with the outcome, and it made an excellent learning experience.

I know a lot of people dislike kubernetes for being over-complicated, but once it's configured it makes deploying new applications ridiculously easy. And, I love when my projects are declaritive, automated, and reproducible. This has given me all of that.


If you like the way I think, please hire me!

As noted, I've been out of work for 9 months. Finding work in today's market has been extraordinarily tough. Like, 0.5% response rate compared to 9% last year tough.

I have a number of referrals available upon request, and one testimonial I'm particularly fond of. Closing out after 7 months working for Remediant, a senior developer said to me: "[I] think you've progressed well beyond junior devops at this point which is a testament to your ability and drive."

Thanks Scott :)

If you're looking for a backend engineer with DevOps chops, or junior-(ish?) DevOps engineer, please take a look at my resume and we can schedule a call.


Onwards, for my cluster I have five... six... uh, -- many projects in mind:

At some point (maybe once some of these projects are finished) I'd like to fork this project and turn it into an afforable and easily reproducible "infrastructure blueprint", a la hackerspace.zone.


In other news, it seems support for the Orange Pi 5 in mainline linux is on its way. Currently, all Orange Pi 5 distros are based on Rockchip's custom 5.10 kernel, which as I recall isn't even really 5.10 in the first place.


The total cost for my cluster was just under $500. That's about $165 including a power supply and 256GB NVMe drive for each machine, plus tax. It's a pretty steep up-front cost, and honestly one machine would have probably sufficed - but then I wouldn't be able to play with DaemonSets or cordon my nodes to update their OS without losing a beat!

Not mentioned earlier, is that I accidentally fried one in a freak wiring incident while attempting to access the serial interface, because I couldn't find my jumper wires. Oops.

The amortized recurring cost of the cluster, given infinite time, is something like $4/mo. But that's only because...

I'm not exposing my home's IP address to the world! I have an nginx proxy on a cheap VPS for about $3.50/mo, and update my home's IP address in that with a simple script.

This also resolves the need for NAT hairpinning, which is notoriously difficult in VyOS.


Fun fact: Chick-fil-A is notorious for running bare-metal kubernetes clusters at each of their restaurants with consumer hardware! I just think this is a really neat idea and a fun piece of kubernetes lore :)


rust enums by example

Ever since I started learning Rust about 4 years ago, I've been in love with its enums. You see, Rust's enums aren't strictly enumerations. They're closer to tagged unions or sum types, which are used to represent variants. Let's take a look at what an enum is, and a few cool use-cases for them.

About enums & A comparison to tagged unions in C

You should already be familiar with unions and traditional enums.

Rust's enums are something of a mix between the two.
You can read up on the basics in the book and/or Rust by Example.
That said, let's look at a simple enum in Rust and how it might be implemented in C.

Note that Rust's enums share the same span of memory between its constituent types, so only a small amount of memory is wasted. You can play around with their internal representations in this playground, or read more in the nomicon.

error handling with variants

Two particularly useful enums are Result and Option.

Result gives you a way to represent whether an operation is successful or not, as well as a way to access the data or error of the... result. šŸ‘€

Option gives you a way to represent whether something exists or not. This is generally used as a replacement for nullable types (which Rust does not have*).

But what really makes Rust shine is that it forces you to explicitly handle enum variants before you can access the underlying data. This is done using the match keyword. Rust also has special syntax for handling Results and Options when you raise issues from the unhappy path.

To start, let's compare how we handle a simple http endpoint with Go's gorilla/mux and Rust's Rocket.

Rust:

Go:

By having data embedded into variants, we can represent whether an operation is successful or not. Potential errors can be propagated, transformed, and handled without mucking up your happy path.

Very cool. šŸ˜Ž

Let's go deeper.

heap allocation and dynamic dispatch

Imagine you're writing an audio system for a game. You have a directed acyclic graph and you need a way to represent the nodes in this graph. A node can be an input (sine wave, mp3), effect (pan, mix), or output (speakers, a file, visualizer).

What all nodes have in common is one function: process(inputs, outputs). Let's call this common behaviour the AudioNode interface (or trait).

So our audio graph looks something like Graph<AudioNode>.
In practice then, each node in the graph is dynamically sized and must be heap allocated. To perform that heap allocation in Rust, our nodes must be wrapped in a smart pointer: Box<dyn AudioNode>.

Then the process(..) function needs to be dynamically dispatched.

All this results in significant overhead with multiple vtable accesses, and more importantly: indirection which prevents compiler optimization.

Keep in mind process(..) is called multiple thousands of times per second.


But we can improve that with enums:

In my project I'm getting up to 10% better performance. Not at all laughable in audio programming. You can remove a lot of boilerplate here with the impl-enum or enum_dispatch crates (see enum_dispatch benchmarks).

Very, very cool. šŸ˜Ž

message passing

Imagine you're writing a music player. Your UI has controls for play/pause, seek, skip, etc. These inputs can come from different places - like dbus, hotkeys, or simple UI interactions.

We've just run into an ideal use-case for MPSC (multi-producer/single-consumer) channels! An MPSC channel is simply an atomic queue that can only be accessed through its producers and consumers.

Whenever one of the aforementioned controls are triggered, we can send a message through an MPSC channel to control playback. With that out of the way, we need to determine what data to send.

Let's look at some potential Java-esque solutions. Normal enums won't work because some of our controls like Seek(timestamp) have associated data. Maybe a class with an enum field, plus fields for each type of associated data would work? Or a string?

It's an oddly gnarly problem to solve.

Fortunately for us, Rust's enums make this easy. This is part of what makes multithreading so nice in Rust.

closing thoughts

The last pattern using enums that I'd like to shine some light on is the finite state machine. Plenty of others have written about state machines in Rust before, so I won't reiterate on that.

Hopefully you've learned something new about rust's enums - whether you've never seen Rust before, or you're a Rust veteran. If you have any questions, feedback, or flattery, you can find my contact info on my rƩsumƩ.


Speaking of rƩsumƩs, I'm looking for work right now!

I'm a generalist software developer with a specialization in backend / web architecture. I've spent the last nine years honing the craft in my free time, and I'd really like to get my foot in the door professionally. Send me an email if you know of any internships, contract positions, or full-time employment that I might be a good fit for!

- Devin

staring into the void

Long ago I went to a 48 hour game jam at Becker University. It was the global game jam and the theme was what do we do now? At the time I had already been working on a 2D game project on top of a javafx canvas, so I copied the text rendering code and said let's make a text adventure!

It was a fun little project, but little did we know the dangers of such a thing. You see, we were in ye olde java 7 times[1], fresh out of our second year of computer science in high school. We had even struggled to represent branching trees of text that could loop back upon themselves. Perhaps better known as directed graphs.

Our solution was to write a big ol' switch statement. Teehee šŸ˜‡ what's a stack overflow?

Text was represented as strings with special tokens to change how fast text was printed. For example, "\^" meant the following text should print quickly, and "\#" meant that text should print slow. This has made many people very angry and has been widely regarded as a bad move.

A few years later, I tried to run the game out on Linux, and, surprise! šŸŽ‰
Audio doesn't play, the logic thread crashes and burns, and you're left with an unresponsive window. Write once, run anywhere, eh?[2]

So 6 years after that, I finally resolved to finish that game the right way. The core idea of void is to have text that is engaging. Sometimes you want text to wiggle, or type-write slowly, or any other of the infinite possibilities to add character to text. Which makes any markup language a natural choice for text representation.

And with that I've started using xml to represent my game text. This required parsing my xml and turning it into data structures stored in bincode files for later use. The last part to navigate then, is branching storylines. I initially decided that this would also be done in xml, so long as the logic isn't much more complicated than checking booleans - but part of me is thinking that maybe these logic checks should be done in Rust.

Anyway, that's all for today! Hopefully I won't get too distracted with other projects in the near future


  1. Technically java 8 had just come out the year before, but we were inexperienced - we didn't even know what a lambda was, let alone how to use it. Frankly, I even thought writing code in a legacy style was considered good practice because it was backwards compatible šŸ¤¦
  2. As long as you're not on linux. And while we're at it, even if you successfully create a cross-platform abstraction layer, you'd need to pack all the abstractions into one distributable. Or create multiple distributables.

autopilot

i've fallen into a pattern of metaphorically losing my mind, regaining it again, and then forgetting - returning to a state of mere existence for months at a time.

you go from thought - and i mean real, genuine thought - ...
you go from genuine thought, and you become just a process.
you may still problem solve, interact with others, et cetera, et al,
but you live on autopilot.

ask yourself: how often do you think about what you're actually doing?

where do you want to go?
how are you going to get there?
what are you actually doing?


we live on some dumb rock flying through space.
we're humans.
we live because we fuck.
--- stop and acknowledge that. ---
yet at least half of our waking lives (at work, university),
procreation is unspeakable.

...

and that's funny. maybe a little sad.
but that's not the point.
the point is that you have to put on a facade, day in and day out.
whether your facade is in regards to sex
or you're embarrassed about your immense plant collection.
you exist almost independent from your true self.
you think differently, you behave differently,
you live on autopilot.

but sometimes, for a little while, you break out.


i had a friend once
who told me that the worst mistake that you can make
is to think that you are alive...

when really, you're asleep in life's waiting room.
ukelele guy in waking life

this is a reminder to wake up.
smell the flowers.
get back to doing what you want to do.
take that trip.
get that project done.
tell her how you really feel.
take off that facade.

don't let the world pass you by.

lightboard follow-up (aka lightboard v2)

Nearly five years ago I finished a lightboard project consisting of a WS2812 LED strip, a sheet of plexiglas, and an adafruit trinket. I never did write a blog post about it, but now is as good a time as ever.

Originally, the 48"x24"x1/8" acrylic sheet was mounted with a 1/4" wide plastic J channel screwed into drywall. The top of the sheet was kept in place with a nail at each corner, allowing the acrylic to slide out easily for maintenance. This mounting solution was not quite good enough, and daily use wore down the drywall until the board could no longer stand.

A few years later, I resolved to make a better lightboard, an advanced lightboard! I built plywood shelves for an unrelated project, and used that to mount the next revision of the lightboard. The new revision uses side-mounted WS2812 LEDs, which can fit perfectly in a 1/8" aluminum J channel.

[Could not load media at startup. The media server may be down.]

With the mounting hardware set up, it was time to work on the electronics. I had spent a good chunk of my free time in the past year writing firmware in Rust for the teensy 3.2. I hoped I could use this firmware to build the lightboard's interface, so I added SPI functionality to the teensy, and... it wasn't fast enough! Turns out, assembly is essential for firmware development. And I really did not feel like learning assembly at the time.

So, I resigned myself to learning C++ and writing the software in that with the help of Arduino libraries.

(holy heck, this is a long post, sorry)

Onto the interface! The plan is to use three rotary encoders and a small OLED screen. Then I'll plop these into a 3D printed enclosure (which I'll figure out later). For now, I've mounted the prototype to some cardboard!~

[Could not load media at startup. The media server may be down.] [Could not load media at startup. The media server may be down.]

The three rotary encoders will control things like the "mode" of the lightboard - on, off, rainbow cycle, etc - parameters of different modes, and resets for those parameters. I haven't perfectly defined the interface yet, but I have plenty of options. Right now I'm thinking each mode will have three parameters, and the mode can be changed by depressing the first knob while turning.

I'm going to end this post here and leave the software for another post.

But first... A celebration!

I've recently graduated with an A.S. in Computer Information Systems, and I've been accepted into UMass Amherst for a B.S. in Mathematics! šŸŽ‰

I'm still not sure about what I'll do in the future, but at least I've accomplished a few things, eh?

Ciao!~

november has come

Wow. I canā€™t believe itā€™s been two years since my last blog post. Iā€™ve learned a lot since the start of this blog. Well, here I am. I read a bunch of books on programming. I got my diploma and started working on a degree. Man, how time flies.

Iā€™m trying to be more conscious of the world around me. Iā€™m using Linux whenever possible, but some classes require otherwise. Luckily spinning up a VM is never too hard. Iā€™ve tested out every desktop environment under the sea before finally settling on Gnome. I still canā€™t find a suitable terminal or file manager. These are two things that macOS seem to get right, but which no-one else can.

Ligatures! I love them. The mozilla foundation is a developerā€™s best friend. FiraMono/FiraCode. Rust. Firefox nightly. Just... Everything. Theyā€™re so great right now! Iā€™m glad all their efforts are catching up to them :)

Iā€™ve also learned a bit of OpenGL. I bought a domain and made a website which blew up on reddit for a minute. I started learning Rust and Kotlin.

The Pokemon project is still ongoing! Itā€™s currently in a private github repository, and I think Iā€™ll release it when the code is pretty enough. Iā€™m refactoring the old code, updating it with new knowledge, and replacing Java with Kotlin as I go. Planning for battles - the largest part of development - is nearly complete. Everything else should move along pretty quickly, and progress should look much faster when that happens.

Iā€™m sure Iā€™m forgetting some things, but this will do for now.

research and whiteboards

Iā€™ve been yelling at myself to write something for a while now, but Iā€™ve been putting it off because I havenā€™t really figured out what to write. So I guess Iā€™ll just wing it and this will be whatever it is.

Over the past however-long-itā€™s-been-since-my-last-post I havenā€™t been doing nothing. I mean Iā€™ve mostly been doing nothing, but I havenā€™t been doing completely nothing. When I reached the point in my Pokemon remake where I had to gather all of the data for the Pokemon I got lazy. I really donā€™t like tedious work, so I kind of ignored it for a while. In the mean time I did a few other things to kill time.

The very first thing I made since Iā€™ve been gone is a glowey-whiteboard. Hereā€™s the final result:

[Could not load media at startup. The media server may be down.] [Could not load media at startup. The media server may be down.]

The whole build needs a post of its own so Iā€™ll try to do that within the next week.

After that I got back on track a bit. I downloaded a bunch of sprites from Veekunā€™s collection of downloads. I also wanted to get the animated sprites from Black/White, so I used HTTP GET requests to gather those from Pokemondb. It took a while and it ended up being something like 65MB total. Not terrible, but I can understand why Veekun wouldnā€™t want that on his server.

At around the same time I finally decided to get all of the Pokemon data up until gen 3. Basically I made a list of all the data needed for each Pokemon (Index #s, names, gender rates, color, egg types, etc.), and put it all into one convenient spreadsheet. I have some different pages for certain data like moves, evolutions, and forms - but I havenā€™t filled out forms and moves yet. I still have to decide how Iā€™m going to program those in first. For forms/transformation Iā€™ve been tossing around this data structure that seems to fit how GameFreak does it. There are three form interfaces: ā€œIn-battle Transformingā€ (Castform, Cherrim, Giratina), ā€œEvolution Transformingā€ (Deoxys, Burmy, Rotom), ā€œNon-Transformingā€ (Unown, Shellos). Thereā€™s probably some worksheet pages that I used just to paste some regex data, so you can ignore those.

I also added the particles for ledge-jumping and running through grass, and Iā€™m in the process of adding battles right now, I just need to figure out how Iā€™m going to implement it. Hereā€™s a beautifully looped gif of the game in its current form.

[Could not load media at startup. The media server may be down.]

I also painted and reorganized my room, and added some neat lights, which Iā€™ll show sometime in the future whenever I get a chance.

Thatā€™s about it for the past. Now onto the future. My current non-Pokemon projects are making a smart mirror from an old laptop screen and some minicomputer, and making a custom aux cable. The smart mirror is fairly straightforward, but the aux cable is where it gets interesting.

Iā€™ve always been frustrated by headphones. Especially expensive ones with hardware you canā€™t replace. The most notably example being the cord, as simple as it is. They always seem to break, usually right at the base of the jack, completely ruining a nice pair of headphones. The solution is usually to cut it off and solder on a new jack, which shortens the cord and usually comes out pretty ugly. Another thing that bothers me is the length of the wire. Theyā€™re either too short for anything but tying an iPod to your arm, or so long that they dangle down to your knees. I might be exaggerating a bit, but it begs the question: why arenā€™t headphone wires made to be retractable? Obviously not buds though.

So Iā€™ve decided Iā€™m going to a.) mount a female TRS jack where the wire splits, and b.) make a custom TRS/aux cable. As a personal choice Iā€™d like to have it with a right-angle jack on one end, a straight jack on the other, and a paracord sheath. I also want it to have a retractable coil. This can all be done by Pexon, but itā€™s cheaper to do on my own and should offer some valuable experience.

The idea is to get some magnet wire, twist those together, wrap that in aluminum coated Mylar to prevent interference, cover that in a polyvinyl sleeve, then cover that in a Nylon paracord sheath, wrap the cable around a metal rod and bake it hot enough for heat memory, then twist it in the opposite direction of the coil to make it snappy, and put on the jacks. Pretty easy. The cable should be 10ā€² long total, and should compress to about 3ā€². Obviously I havenā€™t included all the details, but Iā€™ll give the nitty-gritty when I make a full post on this. Overall it will cost about $50, and Iā€™ll be able to make two cables, with each new one after that costing <$20. All thatā€™s needed is more PVC, Mylar, paracord, and jacks. Much cheaper than the $70+ for a similar cord from Pexon.

I guess thatā€™s about it. I already know what Iā€™ll be working on when I finish all of everything, but Iā€™m going to keep that on the hush hush until I at least finish this Pokemon game. Thatā€™s it for now so until the next time I decide to stop being a lazy bastard, goodbye and goodnight.

gifbackgroundifier

[Could not load media at startup. The media server may be down.]

Finished my ā€œGifBackgroundifierā€! Set an animated .gif as your background on a Windows computer! I think itā€™s pretty rad, and if youā€™d like to try it just ask.


Download GifBackgroundifier.jar

If your GIF doesnā€™t work, try opening it in GIMP and exporting it to a new file with the highest quality. This is just the first version so I havenā€™t made an error popup, but you can tell if your GIF doesnā€™t work by if the loading bar moves or not.

Windows only, sorry.

Feel free to send me ideas, suggestions, or complaints!

Warning: This program uses a decent amount of processing power, about 12% on my laptop.

pocket progress 4

Iā€™ve finally got grass working beautifully, and I actually discovered exactly how grass works through a funky glitch in FR/LG.

Hereā€™s how it works: Use cut in the north-most grass in Route 1, take one step into Viridian city, then go back down to the grass. When you step on the empty tiles, the grass particle effects show and you still have a chance of wild encounters. I figured out that rather than having three separate particles for each ā€œpieceā€ of the animation (the stepped on floor, the fluttering grass/leaves, and the part that stays on above your character), itā€™s all in one particle, that looks like this:

[Could not load media at startup. The media server may be down.]

and changes the depth during the animation. Thereā€™s just one frame in which the second image is behind the character which led me to the discovery.

The glitch isnā€™t listed anywhere I could find easily, which reminds me of another visual glitch in Vermilion City, which is less interesting, but showcases a glaring flaw with the layer system used in the generation 3 games.

[Could not load media at startup. The media server may be down.]

And lastly, hereā€™s a comparison between what my game looks like now, versus what the real game looks like. Pretty close, right? Forgive the input lag on the left screen (The real game).

[Could not load media at startup. The media server may be down.]

pocket progress 3

[Could not load media at startup. The media server may be down.]

Look at how far weā€™ve come! Itā€™s not really that much but itā€™s exciting to me. Here you can see animations for running, biking, and cliff jumping in action! Cliff jumping still needs the offset for the sprite, the shadow, and the dust particles when you land, but the movement and animation frames are all in place!

This gif is a bit lower quality than the last one, even though itā€™s still at 15FPS, but it gets the point across. Anyway, thatā€™s it for now.

Make sure to follow if youā€™re interested in this project. If you want to know whatā€™s being done every day follow my daily updates page.

It isnā€™t shown in the gif, but you can no longer phase through the cliffs going upwards.

pocket progress 2

[Could not load media at startup. The media server may be down.]

Collision and animations are looking good right now. Hereā€™s a gif of where Iā€™m at. It took me all day to figure out what the hell I was doing wrong with interrupting walking into walls.

Anyway, I also overhauled the movement and controls Iā€™d put in yesterday because I realized exactly how the real games handled it.

You can see travelling through areas right in this, which is nice, and you can also see that I havenā€™t programmed in collision for cliffs yet. :x

I had to keep the gif at 15FPS to make sure it was under 2MB while not looking being mangled by compression, but the game does in fact run at 60FPS, just like a real Gameboy game.

Thatā€™s pretty much it for now. Make sure to check the Live Updates page on my blog for live updates of what Iā€™ve done or am doing. Bye!

pocket progress 1

[Could not load media at startup. The media server may be down.]

It may not look like much but this is really exciting progress for me and this project. This is a Pokemon recreation Iā€™m making with Slick2D/Java that Iā€™ve been working on for the past two weeks. Thereā€™s no collisions or animations quite yet but those should be done within the next two days.

This game is a recreation of FR/LG thatā€™s expanded to several other regions in a single story line including Hoenn, Johto, and Orre. The main goal is to have a ā€œpureā€ Pokemon game in which all Pokemon can be found and there are no weird Dex restrictions (For example, not being able to evolve a Crobat in FR/LG before the national dex, or having game specific Pokemon like Vulpix or Bellsprout).

The biggest problem is leveling throughout regions. I think the best solution is to have a level lock until you beat certain gyms - similar to how traded Pokemon of a high level refuse to listen.

If you have any ideas send me an email and Iā€™ll think about it. Remember to check my live update page for more information on what exactly is being done!

the start of something

Last month I started re-playing Pokemon Fire Red on the VBA-M emulator. I went into the game with the goal of catching as many Pokemon as possible. After about two weeks I managed to catch 124 of the 151 in the game. That magic number also happened to be the maximum number of Pokemon you could get in either of the games.

I still wanted to catch all the Pokemon though. Iā€™d never really be happy with it until I had all of them. So I moved my save to Leaf Green and caught all the LG exclusives. This brought me to a total of 138 caught, 149 seen. I would also only capture Female Pokemon and that I have one of every Pokemon in my box in numeric order, but thatā€™s besides the point.

The only Pokemon I couldnā€™t get were the 4 trade-evolvers, the two other starters, the other fossil Pokemon (Omanyte), and the legendary Mew. Omastar and Mew were the only Pokemon I hadnā€™t seen.

And I wanted them.

What really sucks though is that trading on VBA-M is and has been broken forever on FR/LG. I thought: ā€œHey, I program, I should help out with this and figure out the glitch!ā€ so I did just that. Kind of... I opened up the source code in Visual Studio and got it to compile right - Success! After a little while scouring the source I realized that I really didnā€™t know C++ well at all.

I gave up on that and decided I would make my own Pokemon game. That it should run on all systems, be able to use a controller, have online trades, have all the regions, and most importantly that you could catch all the Pokemon.

Five days later and here I am starting this dev blog.

But Iā€™ve never really finished a project this big before, so I donā€™t want this to get popular until I finish. I donā€™t want to disappoint a lot of people. If you find this I beg you not to get too excited until Iā€™ve made considerable progress.

I do want to finish this though, so if anyone stumbles upon this and notices Iā€™m not updating the blog or working on the game, please spam my email or telephone or anything you can.

Email: dwbrite@gmail

Thanks~