Dev Blog N.I.N.E. - No, I'm Not Explaining.

Greetings. It’s me, your co-conspirator and loving great-stepfather, Bites.

Check this shit out.

Let’s get into the update.

As per usual, I’ve been working on stages and backgrounds. I’m going to keep the background stuff secret from you for a little bit longer (until next update, probably!), as Lauren and I are currently working on some pretty cool stuff that we’re not ready to share yet. However, I’m happy to show you some of the stages I’ve made!

stage 1
In this stage, you have to navigate up the slanted floor, and then bounce to the next platform using a ramp at the very end… three times in a row!
stage 2
I wanted a curvy slanted stage where all the turns varied in radius, so you’re constantly kept on your toes on how much to accelerate.
stage 3
Here, you’ll be expected to cross a bunch of tiny, sensitive seesaws to reach a button that reveals the goal… where you spawned! Hopefully, you left yourself a path back home.
stage 4
Be careful when dropping down this steep slope, because you might end up netting yourself so much horizontal speed you get flung past the goals!

I’d also like to make an awesome confirmation:

Minigolf is going to be one of the available party games in Rolled Out!

I prototyped this one by myself, and it was surprisingly easy to program. And of course, a lot of fun, too! If you’re not already in our Discord server, be sure to join - you might be able to catch a live prototyping of a new party game, and perhaps even join in on the fun.

minigolf 1
minigolf 2
minigolf 3

(By the way, these textures aren’t final.)

CraftedCart, as per usual, has a pretty cool writeup for you to sink your teeth into. Even if you aren’t very technically minded, give it a read!!! Do it!!! You have to!!!


Some general tidbits

Not a whole lot of interest has been going on with regards to the code side of things (besides getting rather frustrated with initialization and templates and function pointers and aargh in C++), so this fortnight I’m just going to be going over some miscellaneous smaller bits and pieces that help make the code base nicer to work with and make me feel less like I want to rip my hair out.

Iron Oxide

Rust! For those who haven’t heard of it, Rust (not to be confused with Rust, or Rust) is a programming language unlike most others. Its main premise is a strong focus on safety with minimal overhead, and it achieves through analyzing code during compilation, and rejecting if it the compiler deems it as potentially unsafe.

I’ve been tinkering around with Rust as of recent, making a StepMania font texture generator for a theme I’ve been making, because StepMania’s own generator doesn’t handle some foreign characters at all.

StepMania Font Generator

Anyways, what does it mean for code to be “safe” or “unsafe”? That’s a broad term, but a few examples of dangerous procedures include…

  • Bad memory access/memory leaks - Languages like C and C++ make the programmer have to think about allocating and deallocating chunks of memory. It is really, really easy to mess this up, such as trying to access access a bit of memory has been deallocated, crashing the program. Forgetting to deallocate memory on the other hand leaks memory.
  • Race conditions - In multithreaded code (that is when multiple pieces of code run simultaneously across your CPU cores), it is possible for multiple threads to try and access the same bit of memory simultaneously. Well what happens if one thread is midway through writing to a bit of memory while another thread is midway through reading it?
  • Lack of error handling - It is far too easy to miss error handling sometimes, like forgetting to check an error code. Usually this results in bad memory access later on down the line, as a function returns invalid data or sets some invalid state, and then you try to use such data later on.

So, what’s this got to do with Rust and Rolled Out? Well I find that Rust handles these issues rather well… so well that I’ve taken some ideas from it and implemented it in our C++ code base for Rolled Out!

TResult

Rust almost always forces you to handle errors. Functions that can error in rust will usually return a Result object, which can contain a successful result from computation, or an error. If you want to make use of the successful result, you’re forced to “unwrap” it from the result object, handling errors in the process.

1
2
3
4
5
6
7
8
9
10
// Try to open "hello.txt" - this returns a result
let result = File::open("hello.txt");

// Set the variable "f" to the file handle if opening succeeded, otherwise crash with a message if we failed
let f = match result {
Ok(file) => file,
Err(error) => {
panic!("Problem opening the file: {:?}", error)
},
};

Even if there is no way to handle an error well without terminating the application, terminating as soon as we know something went wrong, with a message too, is easier to troubleshoot for both us as developers, and end users. It’s easier for developers as it’s far easier to see what went wrong if we crash at the source of the problem, rather than later on in the code execution, and it can be easier for end users as logging an error message can help them determine what the issue is if there isn’t a problem with the code.

On the C++ side of things, I found this excellent implementation of a Rust-style Result type for C++, which I’ve taken in to our Rolled Out! code base and modified a bit to work nicer with UE4 and our own error types. Kudos to oktal and Nashenas88 for their work on that. Now we can handle errors as such.

1
2
3
4
5
6
7
8
9
10
11
12
TResult<TSharedRef<FStageConfig>, FGameErrorRef> Res = FStageConfigParser::ParseStageConfigFromFile(ConfigPath);

if (Res.IsOk())
{
TSharedRef<FStageConfig> Config = Res.Unwrap();
// Do stuff with the config
}
else
{
FGameErrorRef Err = Res.UnwrapErr();
// Handle the error somehow (perhaps tell the player that this config is invalid)
}

In the event that the game cannot continue due to an error (such as not having permissions to read the local game database file), Res.Expect(ErrorMessage) works to unwrap the success object or crash the game if there was an error. We’ll even show you what went wrong before crashing.

An error dialog

Hopefully you’ll never have to see anything like that though.

TThreadChannel

When it comes to multithreading, there are a couple ways to communicate between different threads. Approach number one is with shared memory: one thread can modify a bit of memory, and another thread picks up on the change and acts accordingly. This works well but only if you can guarantee that multiple threads aren’t modifying and reading the same chunk of memory simultaneously (Mutexes help with that).

Another approach is to have threads communicate with each other through channels. Imagine an office where you’ve got a stack of papers. Various threads may add more paper to the top of the stack, and you take jobs from the bottom of the paper stack, and when you’re done with your stack of paper you wait patiently for threads to add more work to your pile, because you are a very boring employee.

Ok I’m not quite sure that that analogy was, but that’s the concept of an MPSC (Multiple Producer, Single Consumer) channel. Multiple threads send data into a receiving thread’s job queue, when then processes the results. In Rust, there are separate sender and receiver objects, with one thread having control of the receiver and multiple threads each having their own sender. For Rolled Out, I’ve just made a single “ThreadChannel” object: there wasn’t really a need to split it up into a sender and receiver given I don’t have a compiler to bargain with (The Rust compiler can be a very strict teacher).

I’ve just recently been overhauling stage indexing. Previously, Rolled Out! only ever spun up one thread to index all stages, which just interacted with the game database directly (with shared memory). Now, Rolled Out! will index stages across many different threads - in my case, across 12 threads given I have a 12 thread CPU. When each thread is done with making sense of stage metadata, they will send their results down a thread channel to a 13th thread, whose sole responsibility is to take the results from all 12 threads and shove them one-by-one into the game database.

Channels

This, combined with various other factors (JSON stage configs, competence with using a database…) speeds up stage indexing massively. I’m kinda reluctant to give numbers at the moment given it’s not finished (Currently this only adds stages to the index, it doesn’t check for deleted or modified stages) but we’re sitting around 1.5 to 2 seconds of indexing, even on a hard drive, down from about 30s to a minute with the previous method. Again, take these numbers with a grain of salt, though I wouldn’t expect them to rise too much.

Anyways, that’s about all I have for this post.


My index finger really really hurts for some reason, and I’m experiencing physical pain for every second that I type. With that being said, I think it’s about time I ended the update!

Thank you for reading, and see you on December 1st.