Felix Jones
felixjones.co.uk.web.brid.gy
Felix Jones
@felixjones.co.uk.web.brid.gy
The blog of Xilefian.

[bridged from https://felixjones.co.uk/ on the web: https://fed.brid.gy/web/felixjones.co.uk ]
My Pokémon ROM Hack
Yes, yes even I am guilty of picking up side projects when I have a side projects going on. At least this one is in the Game Boy family! * * * A couple of months ago I was ill with a cold, and I did what most people do when they have a cold: They browse Reddit. I stumbled upon a few posts in the Game Boy subreddits from Redditors showing off their custom made “Pokémon Green” cartridges. For those not in the know: The original Pokémon games released in Japan in 1996 as Pokémon Red and Green, so these custom cartridges were an attempt to restore what is often seen as a lost Pokémon game. Somehow my cold-stricken head decided to lead me into ordering all the materials I would need to create my own “Pokémon Green” cartridge. # If a thing is worth doing This isn’t the first time I’ve come across custom Pokémon cartridges, but I’ve never had a desired to make my own. A lot of the times it is individuals creating custom carts to avoid paying the inflated prices for legitimate, original games, but custom Green cartridges are more interesting. The Japanese releases of Red and Green have differences to the international Red and Blue, not to mention that 1996 is before Pokémon made its international debut, so there was no international branding associated with Pokémon. Combined with the fact that there was no international “Pokémon Green Version”, there’s a gap to be filled. Check-out this BoundaryBreak episode that explores some of the differences: ## It is worth doing well Something I’ve said for a long while regarding retro game projects is that I believe they’re most successful as art pieces, a love letter to a bygone era in gaming. If I were to make a retro game I would want to make a physical cartrige, a box, and a manual to “complete the package” (throw in a poster or something, too). I imagine most people would get enjoyment out of the high quality physical goodies, but as a game developer I get enjoyment at the thought of high quality software being amongst the physical goodies. Now that I have bought into a high quality Pokémon Green display project, I thought I should follow through on my standards and make a high quality Pokémon ROM hack to go with it. # Pokémon ROM hacks Pokémon is quite famous in the ROM hacking scene, specifically the fact that many of the classic games have decompilation projects that make producing ROM hacks a cinch. The pokered project is available on GitHub https://github.com/pret/pokered ## Shin Pokémon Green Whilst the US release of Pokémon Red and Blue have been decompiled, the original Japanese release of Red and Green have not. So I needed a base: Someone who has restored the Japanese content into the pokered project. jojobear13’s shinpokered project does exactly that…And more! The summary lists some of the “Additional Master features that go beyond engine modifications and fixes”, and a lot of these go quite far beyond what the original games provide. I didn’t actually want these additions, I wanted to make something that feels more purposefully pure to the Japanese release of Green. A sort of “What if Pokémon released internationally a year earlier?”. Luckily the project provides a lite branch that only includes fixes and none of the “Additional Master features”. # Are you a boy? Or are you a girl? Shinpokered is quite a well organised project: It’s possible to configure it quite extensively from just the makefile. I noticed that it even keeps some of the code and assets from the “Additional Master features”, in particular the macro `_FPLAYER` enables the code and assets for a female trainer. I decided to break my rule of “What if Pokémon released internationally a year earlier?” and add in an option to select a girl player character. I feel this can still be in the spirit of a “What if” because a female trainer for that generation did exist in the manga, as well as on the cover of the Japanese strategy guides, as covered in this video by The Obsessive Gamer: Let’s take a look at Shinpokered’s female trainer: Unfortunately this character is not the legendary Game Freak character, but is an edit of the boy trainer sprite: I looked around at other implementations of playable girl trainers, and I feel most of them miss the mark. Many of them implement the Leaf character from the Game Boy Advance games, some of them do implement the “lost” female trainer but with spritework that feels closer to modern Pokémon art styles than the original Game Boy art styles. It was becoming clear that I needed to introduce yet another playable girl ROM hack so I could create the character that I was looking for in my custom Pokémon Green ROM hack. # Finding the Style I noticed that the games feature multiple sprites for the rival trainer as the player progresses from the start of the game, all the way to facing the rival as the League champion in the final battle. This provided a gradient of art styles going from a more “junior” starting trainer, an intermediate, and then the champion. I decided that the player is somewhere in the middle as an “intermediate”, so that was the style I was aiming for. Looking back, my first draft reminds me of some of the early Pokémon Gold “dealer” sprites from Game Freak. The pose was inspired by this panel from the Pokémon manga (the “Yellow” arc): When working on digital artwork I use the “mirror” trick where you flip the image you’re working on to gain a fresh look at what you’re working on. It helps with identifying perspective errors, but also results in images that look good even when mirrored, and I ended up keeping the mirrored direction. There was a moment when I realised this lacked any “main character energy”, which is why I added the Pokéball earrings to the character (as shown in the Yellow arc of the manga). I requested feedback from @sarahboev (whom I originally reached out to for this project, before taking it on myself) and they kindly pointed out that the dithering work on the hair is not characteristic of the original Pokémon games: notice that almost all trainer sprites use solid blocks of colour for the hair, and dithering is only used for textiles such as clothing. Here’s what the final sprite looks like on the trainer screen: # Battle Sprite This one was more straight forward than the front sprite, although I did end up with something that looks more fitting for the second generation of games, rather than the first. I may have gone too far with the anti-aliasing, and I brought the character closer to the viewer which probably makes it feel more modern. Regardless, I noticed that the 2x resolution psuedo depth-of-field for generation 1 backsprites makes these details forgivable. The pose is partly inspired from their appearance in the Gold, Silver & Crystal arc of the manga: I did give up with rendering the gloves with the excuse that sprite consistency wasn’t a thing even with Game Freak. # Overworld Sprites These were probably the most difficult sprites to work with as the canvas size is reduced to just 16x16 pixels, and the amount of colours available is reduced to 3. I’m not 100% happy with the result. But I am happy versus my other attempts. Adding the earring to the side sprite very much helped tie the sprite back to the trainer graphic, however I was unable to find a good way to show the earrings in the up/down sprites. Another feature that helped add depth was the light “shine” on the hair. I was holding off from doing this, but seeing a similar hair “shine” on the Pokémon Center attendants convinced me to try it. # Diploma Screen Shinpokered uses the front sprite for the Diploma screen: The original code actually pulls this image from the title screen trainer graphic Shinpokered does not display the female trainer sprite on the title screen, which is something I wanted to change. # Titlescreen Now that I created a unique title screen sprite for the diploma I should use it on the actual titlescreen! But first, the logo: Changing the logo is probably controversial, however I wanted to keep the feeling of a “Pokémon Green” release, but without reverting to the Japanese logo. This might look bland in comparison to the official international logo, but I did borrow the look and font from the same Japanese media that the female trainer originally appeared in: And I am happy this captures the spirit of “What if Pokémon released internationally a year earlier?” ## Girl Titlescreen Sprite I struggled with this sprite, and I’m still not entirely happy with the end result (specifically the leg pose) Here’s what I settled with: The pose is from this image of the character from the Pokémon manga: See how I replaced the Pokédex with an original Game Boy. When I saw the manga image I thought “did the Pokédex even have its design in 1996?” - the Pokédex sprites in Oak’s lab are re-used book sprites and mentions pages I ended up looking through the original Japanese instruction manuals and other media at the time of the release and found no artwork of the Pokédex as we know it. I considered changing the Pokédex out for a book, a Pokéball, or nothing at all - but I like the personality that I’ve given the girl character (in contrast to the very stern-looking boy character), so I tried throwing in some other technology that appears in-game: The original Game Boy! The Game Boy is actually mirrored in the final sprite, this was because I wanted to keep a distinctive A and B button visible, but flipping those ended up blending them into the hand somewhat. ## Random Titlescreen Sprite I could have read the selected player type from the save file and display that on the title screen, but what about cases when creating a new game? I believe the title screen should remain neutral as to the contents of the save file. Also, the changes I’ve made may already not be to everyone’s tastes so I decided to hold back and default display the boy sprite until the player is at the Continue/New Game menu, from there pressing B will roll RNG and display either the girl or boy sprite. That relegates the female title screen sprite to somewhat of an easter egg, and I’m okay with that. # Other Changes ## Script There’s still some gendered language in the script, I’ve left that in, but I have made a small alteration in the Game Freak office: Yes technically the artist didn’t draw this character, and I’m not going to insert myself into the Game Freak offices of the game - so I switched the dialogue to a nod to the history of the “lost female trainer”. * * * There’s a secret that I ported from shinpokered, but I won’t spoil that! ## Gameplay The shinpokered lite improvements are all here, so smarter trainer AI, fixed bugs, etc. ### One quality of life feature… I ported one thing that frustrated me about the original games: This screenshot might not look special to players used to games after the first generation, but at the upper left is a caught Pokémon indicator. That would have been so darn useful for my complete Pokédex runs! ### Starter Pokémon This change I think will be most controversial. To allow the player to mimic the manga scenario, as well as to introduce some variety, when playing as the girl trainer the rival will pick the Pokémon that is **weak** to your choice. This makes the girl trainer a sort of “easy” option, something that I’m okay with as generally the rival battles aren’t much of a problem. I also think this is the type of strange decision that is characteristic of the weirdness of the first generation of Pokémon. ### Default names Girl trainer ROM hacks always change the default names, shinpokered no exception (opting VIOLET, CLAIRE, and JILL). GREEN, LEAF, and CRYSTAL are also common choices, but I wanted default names that felt more in the spirit of the original default names for the Japanese releases of Red, Green, and Blue: * RED/GREEN/BLUE (the edition of the game) * SATOSHI/SHIGERU/TSUNEKAZ * JACK/JOHN/JEAN BLUE makes sense to keep, as that is the name of the manga character in Japan (localised to Green in the west, as Blue was used for the rival). SATOSHI/SHIGERU/TSUNEKAZ are quite male Japaneese names, so for that reason I opted to select a name of a woman known to work at Game Freak during Red and Green’s development: Atsuko Nishida. Luckily JEAN is a gender neutral name, so keeping that choice made most sense. I can see where other ROM hacks decided to use the name JILL as the rhyme of Jack & Jill provides a female counter part name to the default of “JACK”. But I am happy with JEAN. # ROM Hack Release I am currently testing the ROM hack before I release an IPS patch for it. I will also open source my changes as a branch of shinpokered. Follow me on the Fediverse or Bluesky to get notified of the release.
felixjones.co.uk
May 10, 2025 at 11:39 PM
Serialising Binary in C++23
This is the first post in a series that documents the interesting things I encounter as I develop a Game Boy Advance game in 2025 (not yet announced). * * * I am British, so whilst my technical write-up will spell “serial _ize_ ” the British way (serial _ise_) any code samples will be in American English (ie: `serializer::deserialize`). # Serialising Binary in C++23 I do not consider serialisation a difficult task, indeed there’s the naïve tried-and-true technique of manually writing a binary writer and a binary reader for each type: struct my_type { auto serialize(auto& archive) const { archive.write_integer(m_foo); archive.write_string(m_bar); } auto deserialize(auto& archive) const { m_foo = archive.read_integer(); m_bar = archive.read_string(); } private: int m_foo{42}; std::string m_bar{"Hello"}; }; But at work I have recently been writing some code for Minecraft Java Edition, and a feature in that codebase that I’ve always enjoyed is the codecs in the DataFixerUpper library, specifically see the syntax for serialising the `TestData` record: private record TestData( float a, double b, byte c, short d, int e, long f, boolean g, String h, List<String> i, Map<String, String> j, List<Pair<String, String>> k, DayData dayData ) { public static final Codec<TestData> CODEC = RecordCodecBuilder.create(i -> i.group( Codec.FLOAT.fieldOf("a").forGetter(d -> d.a), Codec.DOUBLE.fieldOf("b").forGetter(d -> d.b), Codec.BYTE.fieldOf("c").forGetter(d -> d.c), Codec.SHORT.fieldOf("d").forGetter(d -> d.d), Codec.INT.fieldOf("e").forGetter(d -> d.e), Codec.LONG.fieldOf("f").forGetter(d -> d.f), Codec.BOOL.fieldOf("g").forGetter(d -> d.g), Codec.STRING.fieldOf("h").forGetter(d -> d.h), Codec.STRING.listOf().fieldOf("i").forGetter(d -> d.i), Codec.unboundedMap(Codec.STRING, Codec.STRING).fieldOf("j").forGetter(d -> d.j), Codec.compoundList(Codec.STRING, Codec.STRING).fieldOf("k").forGetter(d -> d.k), DayData.CODEC.fieldOf("day_data").forGetter(d -> d.dayData) ).apply(i, TestData::new)); } That `CODEC` static field is our serialiser, notice that there’s no separate “serialize” and “deserialize” methods, just a definition of what fields there are, what the getters are, what constructor takes these arguments. I really enjoy writing these `Codec` implementations, and the programmer zen of writing DFU Codecs is something I miss when I’m working on Bedrock in C++20. * * * Serialisation is needed for my Game Boy Advance side-project, and this gave me an opportunity to try implementing something closer to DFU’s Codecs than what we traditionally see in the C++ serialiser landscape. I don’t need most of the features of DFU, I only need to write binary data and I don’t even have to worry about versioning data for game updates considering this is a GBA project. # All in on Constexpr I’ve been using `constexpr` a lot for the GBA, and I consider it the primary thing that makes C++ a sensible choice for the GBA. For a serialiser I believe `constexpr` is very important as we can give the compiler all the information about our types and it should, in theory, be able to optimise down to just calling the binary functions for encoding/decoding our types. See what the above Java Codec looks like in my C++ interpretation: struct test_data { float a; double b; unsigned char c; short d; int e; long long f; bool g; static constexpr auto codec = tuple_codec( float_codec.of_member(&test_data::a), double_codec.of_member(&test_data::b), unsigned_char_codec.of_member(&test_data::c), short_codec.of_member(&test_data::d), int_codec.of_member(&test_data::e), long_long_codec.of_member(&test_data::f), bool_codec.of_member(&test_data::g) ).apply<test_data>(); }; The `test_data::codec` static member is entirely built at compile-time, so the order of binary encoding/decoding functions to call for this type should be known to the compiler, letting it in theory optimise this all the way down to just the reading and writing of raw bytes. * * * In my current implementation I can see this: struct foo { int a; int b; int c; int d; static constexpr auto codec = tuple_codec( util::int_codec.of_member(&foo::a), util::int_codec.of_member(&foo::b), util::int_codec.of_member(&foo::c), util::int_codec.of_member(&foo::d) ).apply<foo>(); }; int main() { const std::string serialized = foo::codec.encode(foo{ .a = 1, .b = 2, .c = 3, .d = 4 }); // ... } movs r3, #1 movs r0, r5 add r1, sp, #24 str r3, [sp, #24] bl 0x8000780 <util::append_insert_iterator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::operator=<std::array<char, 4u> >(std::array<char, 4u>&&)> movs r3, #2 movs r0, r5 add r1, sp, #20 str r3, [sp, #20] bl 0x8000780 <util::append_insert_iterator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::operator=<std::array<char, 4u> >(std::array<char, 4u>&&)> movs r3, #3 movs r0, r5 add r1, sp, #16 str r3, [sp, #16] bl 0x8000780 <util::append_insert_iterator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::operator=<std::array<char, 4u> >(std::array<char, 4u>&&)> movs r3, #4 movs r0, r5 add r1, sp, #12 str r3, [sp, #12] bl 0x8000780 <util::append_insert_iterator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::operator=<std::array<char, 4u> >(std::array<char, 4u>&&)> Those four integer encodes are compiled into directly appending four `std::array<char, 4u>` chunks onto the end of a string. I think this is remarkable, we’re able to remove all the cost of the serialisation library down to just reading and writing bytes. # Binary serialise…To a String? In my little snippet above you’ll notice that I serialised to a string: const std::string serialized = foo::codec.encode(foo{ ... }); There’s a feature that `std::string` has that `std::vector` does not: _Small String Optimisation_. Here’s a link to Raymond Chen’s article on the guts of `std::string` that covers this optimisation, but the short of it is that if the data in a string is “small” then it won’t bother with a heap allocation and will actually store the data within the string itself (which is probably on the stack). Boost has small_vector which implements this for vector, but I’m not using Boost and I only need to serialise bytes, and when `sizeof(char) == 1` I might aswell use `std::string` instead of `std::vector<char>`. Fundamental types will serialise to something within a small string, so this optimisation pays off for the GBA where heap allocations occur on the slow 256 KiB RAM. # Encoding & Decoding Fundamental Types The DataFixerUpper library has a set of primitive types that it has encoding operations for: defined in DynamicOps.java. For C++ these would be our fundamental types. Because our fundamental types are `constexpr` themselves, we can implement their encode/decode with `std::bit_cast` to and from an array of bytes: template<typename T> struct copy_encoder { template<std::convertible_to<T> O, AppendInserter Out> static constexpr void operator()(O&& obj, Out&& out) { out = std::bit_cast<std::array<char, sizeof(T)>>(std::forward<O>(obj)); } }; template<typename T> struct copy_decoder { template<std::input_iterator In> static constexpr auto operator()(In&& in) { std::array<char, sizeof(T)> data; for (auto& c : data) { c = *in++; } return std::bit_cast<T>(data); } }; Actually these copy encoders are good for anything that is `std::bit_cast` compatible (which is anything that is `std::is_trivially_copyable_v`), so the fundamental types can all use a `trivially_copyable_codec`: template<typename T> requires std::is_trivially_copyable_v<T> constexpr auto trivially_copyable_codec() { return codec(copy_encoder<T>{}, copy_decoder<T>{}); } inline constexpr auto int_codec = trivially_copyable_codec<int>(); inline constexpr auto unsigned_int_codec = trivially_copyable_codec<unsigned int>(); inline constexpr auto short_codec = trivially_copyable_codec<short>(); inline constexpr auto unsigned_short_codec = trivially_copyable_codec<unsigned short>(); inline constexpr auto long_codec = trivially_copyable_codec<long>(); inline constexpr auto unsigned_long_codec = trivially_copyable_codec<unsigned long>(); inline constexpr auto long_long_codec = trivially_copyable_codec<long long>(); inline constexpr auto unsigned_long_long_codec = trivially_copyable_codec<unsigned long long>(); inline constexpr auto float_codec = trivially_copyable_codec<float>(); inline constexpr auto double_codec = trivially_copyable_codec<double>(); inline constexpr auto long_double_codec = trivially_copyable_codec<long double>(); inline constexpr auto char_codec = trivially_copyable_codec<char>(); inline constexpr auto signed_char_codec = trivially_copyable_codec<signed char>(); inline constexpr auto unsigned_char_codec = trivially_copyable_codec<unsigned char>(); inline constexpr auto wchar_codec = trivially_copyable_codec<wchar_t>(); inline constexpr auto char8_codec = trivially_copyable_codec<char8_t>(); inline constexpr auto char16_codec = trivially_copyable_codec<char16_t>(); inline constexpr auto char32_codec = trivially_copyable_codec<char32_t>(); inline constexpr auto bool_codec = trivially_copyable_codec<bool>(); If we have a type that is trivial then we have the option of a simple byte copy codec: struct my_type { static consteval auto codec() { return util::trivially_copyable_codec<my_type>(); } int a; int b; }; ## The Arguably Fundamental Types I’ll never argue that `std::tuple` is a C++23 fundamental type, but things being tuple-like is quite fundamental in writing C++. Structs and Classes can be argued as being fancy tuples, so to support serialising these I have a generic `tuple_codec`. template<typename... Codecs> struct tuple_encoder { constexpr explicit tuple_encoder(Codecs... codecs) : m_encoder{ codecs.encoder()... } {} template<has_std_get T, AppendInserter Out> constexpr void operator()(T&& obj, Out&& out) const { [&]<std::size_t... I>(std::index_sequence<I...>) constexpr { (std::invoke(std::get<I>(m_encoder), std::get<I>(std::forward<T>(obj)), std::forward<Out>(out)), ...); }(std::make_index_sequence<std::tuple_size_v<decltype(m_encoder)>>{}); } template<typename T, AppendInserter Out> constexpr void operator()(T&& obj, Out&& out) const { [&]<std::size_t... I>(std::index_sequence<I...>) constexpr { (std::invoke(std::get<I>(m_encoder), std::forward<T>(obj), std::forward<Out>(out)), ...); }(std::make_index_sequence<std::tuple_size_v<decltype(m_encoder)>>{}); } protected: std::tuple<encoder_type<Codecs>...> m_encoder; }; Notice how the tuple encoder has an overload for using `std::get<>` versus forwarding the object reference to all of the tuple element encoders. The latter overload is the mechanism that handles serialising member variables (the former is the true serialiser for tuple-like). But what’s the point of forwarding the same object to all the encoders? If the encoders were aware of the incoming object type, and also knew of a member function or member variabel of that object, then it can choose to encode that specific member of the object and nothing else. This is what the `codec<>::of_member` function is for. * * * The tuple decoder calls `std::make_tuple` using the results of the tuple element decoders. template<typename... Codecs> struct tuple_decoder { explicit constexpr tuple_decoder(Codecs... codecs) : m_decoder{ codecs.decoder()... } {} template<std::input_iterator In> constexpr auto operator()(In&& in) const { return [&]<std::size_t... I>(std::index_sequence<I...>) constexpr { return std::make_tuple(std::invoke(std::get<I>(m_decoder), std::forward<In>(in))...); }(std::make_index_sequence<std::tuple_size_v<decltype(m_decoder)>>{}); } protected: std::tuple<decoder_type<Codecs>...> m_decoder; }; This certainly works for tuple-like, but what about our objects with serialised member variables? That’s where the `apply` comes into it, which adds decoders specific for member variables: ### Applying lambdas template<typename Decoder, typename Ctor> struct apply_lambda { constexpr apply_lambda(Decoder decoder, Ctor constructor) : m_decoder{ decoder }, m_constructor{ constructor } {} template<std::input_iterator In> constexpr auto operator()(In&& in) const { if constexpr (const auto result = std::invoke(m_decoder, std::forward<In>(in)); std::invocable<decltype(m_constructor), decltype(result)>) { return std::invoke(m_constructor, result); // For the case where we actually want a tuple as an argument } else { return std::apply(m_constructor, result); // For the case where we want to forward each tuple argument } } protected: Decoder m_decoder; Ctor m_constructor; }; The `apply_lambda` forwards the `std::make_tuple` result into the parameters of a given lambda, allowing for something like: struct foo { int bar; int baz; static constexpr auto codec = tuple_codec( util::int_codec.of_member(&foo::bar), util::int_codec.of_member(&foo::baz) ).apply([](int bar, int baz) constexpr { // We could assert that our parameters are within constraints here return std::make_unique<foo>(bar, baz); // Always deserialises to a unique_ptr }); }; ### Applying tuples template<typename Decoder, typename T> struct apply_tuple { constexpr explicit apply_tuple(Decoder decoder) : m_decoder{ decoder } {} template<std::input_iterator In> constexpr auto operator()(In&& in) const { return std::make_from_tuple<T>(std::invoke(m_decoder, std::forward<In>(in))); } protected: Decoder m_decoder; }; The `apply_tuple` forwards the result of `std::make_tuple` to `std::make_from_tuple`, which will call whatever constructor for type `T` matches the given tuple arguments. struct foo { int bar; int baz; static constexpr auto codec = tuple_codec( util::int_codec.of_member(&foo::bar), util::int_codec.of_member(&foo::baz) ).apply<foo>(); // Fowards to apply_tuple }; ### Applying ranges template<typename Decoder, typename T> struct apply_range { constexpr explicit apply_range(Decoder decoder) : m_decoder{ decoder } {} template<std::input_iterator In> constexpr auto operator()(In&& in) const { return std::ranges::to<T>(std::invoke(m_decoder, std::forward<In>(in))); } protected: Decoder m_decoder; }; Applying ranges only makes sense if we can support encoding ranges: template<typename Codec, typename IndexCodec> struct range_encoder { constexpr range_encoder(Codec codec, IndexCodec indexCodec) : m_encoder{ codec.encoder() }, m_indexEncoder{ indexCodec.encoder() } {} template<typename T, AppendInserter Out> requires std::ranges::range<T> constexpr void operator()(T&& range, Out&& out) const { std::invoke(m_indexEncoder, std::ranges::size(range), std::forward<Out>(out)); std::ranges::for_each(range, [&]<typename O>(O&& obj) constexpr { std::invoke(m_encoder, std::forward<O>(obj), std::forward<Out>(out)); }); } protected: encoder_type<Codec> m_encoder; encoder_type<IndexCodec> m_indexEncoder; }; We can now serialise some standard containers: static constexpr auto vector_of_int_codec = range_codec(util::int_codec).apply<std::vector<int>>(); static constexpr auto list_of_int_codec = range_codec(util::int_codec).apply<std::list<int>>(); static constexpr auto string_codec = range_codec(util::char_codec).apply<std::string>(); static constexpr auto key_value_codec = range_codec(tuple_codec(string_codec, string_codec)); ### Applying ranges of tuples The final step to this would be supporting `std::map`-like containers, which requires applying a range of tuples. template<typename Decoder, typename T> struct apply_range_of_tuple { constexpr explicit apply_range_of_tuple(Decoder decoder) : m_decoder{ decoder } {} template<std::input_iterator In> constexpr auto operator()(In&& in) const { return std::ranges::to<T>(std::invoke(m_decoder, std::forward<In>(in))); } protected: Decoder m_decoder; }; This applies to constructors such as `std::unordered_map`: static constexpr auto map_codec = range_codec( tuple_codec(int_codec, int_codec) ).apply<std::unordered_map<int, int>>(); It was a very cool moment when writing the above worked first time, as I was expecting to be writing specialisations for `std::map`-like support. # Now my compile times are long These codecs add a good second or so to my compile time, and that could be unacceptable for larger projects. ## And the error messages are even longer There is some very deeply nested templates at work here, so when a compile error does happen the resulting message is just impossible for a human to decipher. This has probably slowed down development of my binary serialiser the most.
felixjones.co.uk
March 1, 2025 at 10:47 PM
The Coolest Tribool
A common function in the GBA community is to convert the directional pad buttons into a signed integer value for each axis. If Left is pressed, a `-1` is returned, if Right is pressed, a `+1` is returned. If both, or neither, are pressed: `0` is returned. This is often called a “tribool”, AKA a 3-state value. When represented with `-1`, `0`, `+1`, the tribool can be directly added to a position value to move a character left or right. Very handy. # Implementing Tribool Assume our tribool is constructed from a bitmask, where bit 0 is `+1`, and bit 1 is `-1`. ## First version Most programmers would probably write something like this as their first attempt: int tribool(int mask) { const int bit0 = mask & 1; const int bit1 = (mask >> 1) & 1; if (bit0 == bit1) return 0; return bit0 ? 1 : -1; } Compiled with GCC `-O2` the 32-bit ARM code is: tribool: asr r3, r0, #1 and r3, r3, #1 and r0, r0, #1 cmp r0, r3 beq .L3 cmp r0, #0 mvneq r0, #0 bx lr .L3: mov r0, #0 bx lr ## Tonclib version The libtonc implementation looks like: int tribool(int mask) { const int bit0 = mask & 1; const int bit1 = (mask >> 1) & 1; return bit0 - bit1; } Compiled with GCC `-Og` the 32-bit ARM code is: tribool: and r3, r0, #1 asr r0, r0, #1 and r0, r0, #1 sub r0, r3, r0 bx lr This looks astonishingly optimised, however I suspected we could do better. # Sign extending On the GBA the key indices for each axis on the dpad, and the shoulder buttons, are next to each other. I noticed that simply sign-extending the upper bit will get us somewhat close to a tribool implementation: int almost_tribool(int mask) { return (mask << 30) >> 30; } For an 8-bit number, this sign extend produces the following states: 2-bit Mask | 8-bit Sign Extended | “Tribool” State ---|---|--- 0b00 | 0b00000000 | 0 0b01 | 0b00000001 | 1 0b10 | 0b11111110 | -2 0b11 | 0b11111111 | -1 Unfortunately this does not respect the exclusivity requirement of our tribool function: When both bits are equal (`0b00` or `0b11`), the function must always return `0`. But this is close, and for original GBA hardware where it is physically impossible to hit both left and right buttons at the same time this almost works (as long as you only do sign comparisons or compare against zero). For anyone interested the GCC `-Og` 32-bit ARM code is: almost_tribool: lsls r0, r0, #30 asrs r0, r0, #30 bx lr A simple sign-extend. ## Fixing up the bits My first instinct was to analyse the possible states, and figure out how to manually fix the `-2` and `-1` states. I noticed that just adding `1` gets these into range, however this breaks the `0` and `1` cases: 2-bit Mask | 8-bit Sign Extended + `1` | “Tribool” State ---|---|--- 0b00 | 0b00000001 | 1 0b01 | 0b00000010 | 2 0b10 | 0b11111111 | -1 0b11 | 0b00000000 | 0 So we should only add `1` for the negative cases, conveniently the cases with a sign-bit set: int tribool(int mask) { mask = (mask << 30) >> 30; if (mask >= 0) return mask; return mask + 1; } Compiled with GCC `-O1` the 32-bit ARM code is perfect: tribool: lsl r0, r0, #30 asrs r0, r0, #30 addmi r0, r0, #1 bx lr The `addmi` instruction is conditionally executed only when the `mi` CPU flag (the minus flag) is set, which the preceeding `asrs` instruction sets if the result or arithmetically shifting right is a negative/minus number. The 16-bit thumb code with GCC `-O1` is equally as good: tribool: lsls r3, r0, #30 asrs r0, r3, #30 lsrs r3, r3, #31 adds r0, r0, r3 bx lr In this code, the second `lsrs` converts the sign bit into the number `1` and adds that to the original value. ### What about `-Og`? Notice that I showed the GCC `-O1` assembly output for the fixed implementation of sign-extended tribool. For GDB debugging on embedded systems, especially retro consoles like the GBA, you should really be using `-Og`. GCC `-Og` 32-bit ARM: tribool: lsl r0, r0, #30 asrs r0, r0, #30 bxpl lr add r0, r0, #1 bx lr GCC `-Og` 16-bit thumb: tribool: lsls r3, r0, #30 asrs r0, r3, #30 cmp r3, #0 bge .L1 adds r0, r0, #1 .L1: bx lr The ARM code isn’t too bad, with `bxpl` returning if the result is positive, but the thumb code now has a full-fat branch. We can further fix this by explicitly writing out the 16-bit thumb logic in C: int tribool(int mask) { mask = (mask << 30) >> 30; int fix = (unsigned int) mask >> 31; return mask + fix; } Compiled with GCC `-Og` 16-bit thumb: tribool: lsls r3, r0, #30 asrs r0, r3, #30 lsrs r3, r3, #31 adds r0, r0, r3 bx lr Compiled with GCC `-Og` 32-bit ARM: tribool: lsl r0, r0, #30 lsr r3, r0, #31 add r0, r3, r0, asr #30 bx lr This is the best we can get with 32-bit ARM, with 1 instruction fewer than Tonclib. For 16-bit thumb, there is one more optimisation available. # Register pressure There’s never enough registers on a CPU, so often a lot of time is spent juggling registers on the stack. If we’re inlining these functions (which for sometihng this small, you probably should be) then optimising how many registers are used becomes important. The problem is the temporary used: int fix = (unsigned int) mask >> 31; The `fix` value depends on `mask`, and our `mask + fix` return value depends on both, so two registers will be used. If we could remove `fix` then the compiler will be able to operate solely on the `mask`. ## Analysis of the bits Looking back at the 8-bit sign extended binary: 2-bit Mask | 8-bit Sign Extended | “Tribool” State ---|---|--- 0b00 | 0b00000000 | 0 0b01 | 0b00000001 | 1 0b10 | 0b11111110 | -2 0b11 | 0b11111111 | -1 The two correct states (`0` and `1`) only differ by bit index 0 (the lowest bit), and the two incorrect states (`-2` and `-1`) differ by _the same_ bit index 0. If we were to shift right by 1 bit with sign extend then we can separate the correct and incorrect states into two values: 2-bit Mask | 8-bit Sign Extended | Shifted right 1 | Fix State ---|---|---|--- 0b00 | 0b00000000 | 0b00000000 | 0 0b01 | 0b00000001 | 0b00000000 | 0 0b10 | 0b11111110 | 0b11111111 | -1 0b11 | 0b11111111 | 0b11111111 | -1 The correct states are now `0`, and the incorrect states are now `-1`, we can subtract by this `-1` to apply our `+ 1` fix from earlier: 2-bit Mask | Sign Extended | Fix State | Sign Extended - Fix State ---|---|---|--- 0b00 | 0 | 0 | 0 - 0 = **0** 0b01 | 1 | 0 | 1 - 0 = **1** 0b10 | -2 | -1 | -2 - -1 = **-1** 0b11 | -1 | -1 | -1 - -1 = **0** And now we have our desired tribool. The C for this is: int tribool(int mask) { mask = (mask << 30) >> 30; return mask - (mask >> 1); } But wait, this still internally requires a temporary register for `mask >> 1` to be added to the original `mask`, so all we’ve done is hide the register in C. ## Ones’ complement rabbit hole After staring at the binary for a while I noticed that the two incorrect states are **ones’ complement** of the actual state I needed them to be in. So this whole time I was trying to figure out how to convert 2-bit ones’ complement negative numbers into 2-bit twos’ complement. The Wikipedia articles for both ones’ complement and two’s complement has good information: * Ones’ complement * Two’s complement This took me down the rabbit hole of trying to convert using a combination of bit flips (`~mask`) and two’s complement negation (`-mask`), but nothing I tried was an improvement. # The coolest tribool I went back to the analysis of the `+ 1` fix: 2-bit Mask | 8-bit Sign Extended + `1` | “Tribool” State ---|---|--- 0b00 | 0b00000001 | 1 0b01 | 0b00000010 | 2 0b10 | 0b11111111 | -1 0b11 | 0b00000000 | 0 After spending so long staring at the bits, I noticed something: The values I was looking for were right there all along! 2-bit Mask | 8-bit Sign Extended + `1` ---|--- 0b00 | 0b00000**00** 1 0b01 | 0b00000**01** 0 0b10 | 0b11111**11** 1 0b11 | 0b00000**00** 0 Just right shifting one more place (with sign extend) gets the results I had been looking for. 2-bit Mask | 8-bit Sign Extended + `1` | Shift Right | Tribool State ---|---|---|--- 0b00 | 0b00000001 | 0b00000000 | 0 0b01 | 0b00000010 | 0b00000001 | 1 0b10 | 0b11111111 | 0b11111111 | -1 0b11 | 0b00000000 | 0b00000000 | 0 The C for this is the obvious: int tribool(int mask) { mask = (mask << 30) >> 30; return (mask + 1) >> 1; } Compiled with GCC `-Og` 16-bit thumb: tribool: lsls r0, r0, #30 asrs r0, r0, #30 adds r0, r0, #1 asrs r0, r0, #1 bx lr Compiled with GCC `-Og` 32-bit ARM: tribool: lsl r0, r0, #30 asr r0, r0, #30 add r0, r0, #1 asr r0, r0, #1 bx lr Notice that the assembly for both thumb and ARM only operates on `r0`, which is the input parameter `mask`. ## Final implementation 32-bit ARM mode has more available registers than 16-bit thumb, so optimising for register pressure isn’t necessarily the best choice. My final implementation selects between optimising for code size with 32-bit ARM, and register pressure for 16-bit thumb. int tribool(int mask) { mask = (mask << 30) >> 30; // Sign extend high bit #if defined(__arm__) && !defined(__thumb__) return mask - (mask >> 1); // Optimise for code size #else return (mask + 1) >> 1; // Optimise for register pressure #endif } Compiling for GCC `-Og` on x86_64 will select the register pressure optimisation: tribool: mov eax, edi sal eax, 30 sar eax, 30 add eax, 1 sar eax ret ## Inverting the tribool The GBA keypad register is actually bit inverted to what most programmers would expect, so a button pressed is `0`, and a button released is `1`. Most developers invert the keypad mask after reading it, so they can test `mask & KEYLEFT` and get the result they expect. This extra invert isn’t strictly necessary (especially if you’re using C++ which can wrap the raw data with accessing member functions), but the tribool implementation needs to be inverted to account for `0` being our “true” value, and `1` being our “false” value. int inv_tribool(int mask) { mask = (mask << 30) >> 30; // Sign extend high bit #if defined(__arm__) && !defined(__thumb__) return (mask >> 1) - mask; // Optimise for code size #else return (0 - mask) >> 1; // Optimise for register pressure #endif } Notice the symmetry with the regular function. The thumb and ARM assembly is just as sensible: 16-bit thumb: inv_tribool: lsls r0, r0, #30 asrs r0, r0, #30 rsbs r0, r0, #0 asrs r0, r0, #1 bx lr 32-bit ARM: inv_tribool: lsl r0, r0, #30 asr r3, r0, #31 sub r0, r3, r0, asr #30 bx lr # Compiler Explorer link godbolt.org/z/z6Y6efb9h
felixjones.co.uk
November 9, 2023 at 12:00 AM
Mob Vote 2023 post-mortem
Yesterday was the canonical two-thousand-and-twenty-third Mob Vote. Did you know: The first mob vote was hosted by Roman emporer Tiberius Julius Augustus in the year 1 AD to decide which of three beasts shall be thrown into Gladiatorial battle against Stevus Maximus Aurus; a fighter who gained fame through defeating a squid using nothing but a fishing rod. Probably. Opinions expressed here are my own, they are not representative of Mojang Studios, nor the Minecraft team. Full transparency: I do not have any _direct_ involvement in Minecraft Live, nor the mob voting itself, so I am likely talking out of ignorance regarding how things work and my opinions are probably going to be garbage as a result of that ¯\\_(ツ)_/¯ # What _was_ I involved with? Unlike previous years; this year I didn’t first learn about the mobs via drinking boxed wine with colleagues in Vasaparken. I had the Bedrock Gameplay Tech Lead responsibility of signing off on the mob proposals, giving me the power to make things very difficult for the mob designers by rejecting their ideas with any kind of tech related excuse - if I _really_ wanted to >:D Specifically what I was signing off on was “is this idea technically feasible given our time and resources”. # My Office Conversations ## Which mob will you vote for? Between the handful of colleagues I asked, it seemed like the battle will be between the Crab and the Penguin. Both sides argued that their mob was “cute”, which seems to be the deciding factor amongst the handful of Mojang staff that happened to be in conversation with me. ## Which mob do you _think_ will win? This is a very different question, but again it seemed like the battle will be between the Crab and the Penguin. The arguments were: * Crab has a utility to help players, and players always vote for the useful mob * Penguin is a real cutie, and players always vote for the cute mob I felt pretty alone when I argued the Armadillo would win, but who’s laughing now? # The Armadillo Won the Mob Vote The Armadillo won the mob vote. # Were the 2023 Mobs Better Balanced? Again: I wasn’t involved in this, other than signing off on the technical proposals, so I have no clue how much thought or effort was put into balancing the mobs versus previous years, but as a Minecraft fan it looks to me that some serious effort was put into avoiding having “one obvious choice” that I believe affected previous mob votes. I do wonder if the mobs have indeed been simplified versus the Allay or Sniffer in terms of complexity to keep things balanced. Of course, the winning mob will probably have a load more complexity thrown into it as its design is explored (I hope not because that means more work…), but I believe what we presented this year was kept simple and that’s what provided the balance. We got the mob, a quirk, the location, and 1 feature: Mob | Quirk | Biome | Feature ---|---|---|--- Penguin | Social amphibian | Stony shores | Somehow makes boats fast Armadillo | Curls into a ball | Savanna | Scute makes wolf armour Crab | It’s a crab. | Mangrove swamp | Somehow claw places blocks further away ## A world without “one obvious choice” Last year I wrote that I saw a handful of comments along the lines of “But seeing the other two mobs lose makes me upset”, and I specifically said “I’m probably not communicating my point here very well, probably because I need more time to think on it.” I now think that this sentiment from years past came from individuals who believed past mob votes were balanced and were faced with the reality that no matter the winner: it is a 66% loss - and I believe with the mobs being even more balanced this year this sentiment grew to be the dominant discussion on Twitter. If there was one obvious choice, then the results of the vote is not a 66% loss, it’s a 0% loss because one of the mobs was clearly better than all the others, and comparatively fewer people would be upset about that. The ceremonial yeeting of the mobs. I think this also plays into the sentiment of “the mob vote just divides the community”. If all mobs had a 33% chance of winning, then the community would be divided into thirds, each third championing their mob. Both of these sentiments are what I’ve personally seen on my Twitter feed this year, leading me to conclude that we got the balance quite close. ## Why did the Armadillo win? If the mobs were perfectly balanced, then the vote would tie on a 33% split each (which would have been incredible). I remember leaving the meeting where the mobs were first presented to me for tech approval, turning to someone and saying “The Armadillo will win because of the Wolf armour”. I asked my Armadillo-voting followers for some info on why they chose this mob and I’ve seen two major points: ### Savanna is empty I’ve seen comments that the savanna is empty, lacks identity, lacks a mob, and in general “needs an update”. Indeed, adding a unique mob to this biome will help, but I’d argue the same applies for the Penguin with the stony shores - that being said the savanna is a more visually interesting biome to build in versus the stony shore. ### Wolf Armour I’m probably cherry picking this reason because it’s the one I predicted, but to my, rather-biased, eyes it does indeed look like wolf armour was the deciding factor. I believe more wolf variations (more specifically, dog breeds) is a very popular suggestion from the community, and in general players care a lot for their Minecraft pets, so with wolf armour taking a step towards pet accessories it probably provides more empathetic theming than a crab claw and boats. The actual protection the armour provides is important, too, but I wouldn’t be surprised if players are more interested in the aesthetic potential. > The armadillo received over 40% of the over 5 million votes! 📷 > > Mob Vote 2023 results: > Armadillo – 42.3% > Crab – 32.5% > Penguin – 25.2%#MinecraftLive 🎉 pic.twitter.com/WmhKVJWJfc > > — Minecraft (@Minecraft) October 15, 2023 # More Community Reactions Important! I am not part of the Mojang community team: I am just a dude on Twitter, picking his nose as he scrolls through Minecraft tweets, like any other Minecraft player. I’ve tried to compile some of the reactions I’ve personally seen, with some of my personal thoughts. ## Mojang is locking these crucial features behind the mob vote I was surprised to see this comment appear in Twitter, because personally I don’t believe these features to be crucial, and I think that’s important for a mob vote specifically because Mojang would otherwise be locking crucial features behind a vote. It’s pretty easy to use the arguments that players have presented against each mob to counter the idea that these are crucial features: * “Crab-claw reach can be achieved by moving closer” * “If you need to protect your wolf, you’d leave it at home” * “Dolphins can already speed up swimming” That being said, I think there is a learning _somewhere_ here for the Minecraft team regarding the perception of features being critical to the game. ## Players who don’t even play Minecraft that much shouldn’t be allowed to vote An evolution of “no votes from people who don’t own Minecraft”. I have the (very) controversial opinion that even people who watch Minecraft videos without owning the game should be allowed to vote for which mob their favourite content creator gets to interact with. It seems like a mistake to gate keep the mob vote behind play time, although perhaps some kind of interesting game could be made out of this: The more Diamonds you mine the more votes you get? I can imagine this working with the Bedrock Mob Vote map, where perhaps players could take part in an Infiniminer style PvP game of mine the most Diamonds, which are then traded in for mob votes. Maybe Mojang should provide some kind of test to see how pro a Minecraft player is before they can vote? I don’t think so. ## Players always vote for the cute mob I think this is more in vein with why I think Wolf armour was a significant factor, where players are voting for something that they can empathise with. That being said, the Penguin and Crab are both also rather cute. As I wrote above: the office prediction was that the Crab and Penguin will be split on the argument of Utility vs Cute. Looking at the 2020 vote: the content creator communities really gathered around and agreed on the Iceologer as being the “correct” choice to vote for. Most of the discussion I saw were around the utility provided by a hostile mob and whatever item it would probably drop, and I remember in the office we expected an Iceologer sweep, to the point where Ulraf’s reaction when unveiling the Glow Squid was a genuine reaction of surprise. Wait, was the Glow Squid more cute than the Moobloom? Perhaps cute does trump utility, but I’m not sure that’s a _bad_ reason to vote for a mob. The mob is going to be in the game for another 85 years or something, so it should probably be one that players enjoy just hanging around and seeing for the long-run. Chances are: any utility provided will be made obsolete by some random update made within the next 50 years. This probably speaks to the different ways that players play the game: Some players really do focus on the technical aspects, where utility is the most important factor. If all mobs were equally cute as they were useful, then we’d be golden. ## Mojang staff shouldn’t be allowed to vote Whilst this one is pretty silly as there’s millions more players than there are staff at Mojang (let alone, people in the Minecraft team), there is probably something to be said about Mojang staff supporting particular mobs on Twitter. Mojang staff are likely to have inside information, and perhaps there is a biased preference that the Minecraft team would rather work on one mob than the other, so then there’s motivation to encourage your following to vote for your mob of choice. Of course, this isn’t a sensible line of thinking. The reality is only a handful of people at Mojang are involved with the mobs themselves, and the vast, vast majority of Minecraft players have no social interaction with any Mojang staff. Plus, it’s just the Minecraft mob vote: It really doesn’t matter. # Would we be better without a Mob Vote? On my personal Twitter, I encountered a lot of individuals expressing the sentiment I mentioned above: The mob vote is heart breaking because no matter which mob wins, it’s a 66% loss with the other two mobs. This naturally led to “Give us all three mobs this year”, and then “Unpaid modders can add these mobs to the game within hours”. There’s even a petition to this endeavour, which has gone quite viral. A lot has already been said about comparing skilled modders vs Mojang elsewhere, including my post last year (that you should read), but I’ll cover one part from last year’s post-mortem again here: ## Why have a mob vote? Like the rest of this post, the following is my personal opinion and does not reflect the opinions of Mojang Studios nor the Minecraft team. For many people, the idea of developing a video game, or working in the games industry, is a far-off dream and many write it off as an impossibility. The mob vote is the chance for players to influence the biggest video game in human history. When I think of the mob vote, I think about all the offline communities talking about Minecraft and discussing which mob to vote for and why. Most Minecraft fans do not have Twitter, or Discord, or a voice on YouTube; they are with their friends talking about Minecraft, and they’re hopefully talking about the experiences that a mob brings to their game. Maybe they talk about the game design, maybe the artwork, maybe the personality in the animations, or they’re focused on the utility that the mob brings to their game. All of these are game development competencies - and players’ discussions and explorations get to be applied with their mob vote: influencing the development of Minecraft. It’s a tiny taste of being a game developer: Mojang has the unique opportunity to sow that seed into millions of Minecraft players across the planet once a year. We’re up here with the best game ever made and we should be inviting others to join us. Starting with the players who aren’t even thinking about being game developers, but are currently talking about the design of a Minecraft mob with their friends right before the mob vote. I admit, it’s a very idealistic way of looking at the Minecraft mob vote. Pessimistically, it’s just community engagement, but to me it’s one of those moments where we build a better world. In my personal opinion: removing the mob vote would be a net loss. * * * Just a few of my thoughts on the Minecraft 2023 Mob Vote. If you want to hear the rest of my thoughts, then join Mojang, get your NDA signed, and ask me in person.
felixjones.co.uk
October 16, 2023 at 12:00 AM
Krispig Blomkål recipe - Chef Felix
Ah, the old cauliflower. Did you know that the Swedish for flower is blomma? Did you also know that the Swedish for cabbage is kål? And did you know that cauliflower, coleslaw, and kale all come from the same Latin word “caulae”? That’s right: Blomkål is flower-kale, or flower-cauli, or cauliflower. A while back we used Hello Fresh (not sponsored) for a few months, and this is one of the better recipes that we got, to the point where I’ve cooked it more times than I’ve counted. Unfortunately Hello Fresh’s instructions are close to garbage (do they even account for ingredient preperation time???), not to mention my recipe is in Swedish so I can’t even understand the instructions. I’ve decided to type it up proper and translate it. I’ll add my own artistic flair, so hopefully Hello Fresh doesn’t mind. * * * Recipe is for two, double everything for four, double again for eight, multiply by 3 for six, take the square root if you’re feeling irrational. Recipe will successfully feed vegetarians. # Ingredients Hot tip: Prepare ingredients before cooking to give yourself the illusion that you’re saving time. If you’re cooking immediately after ingredient prep; then pre-heat your oven to 200°C. ## Breaded cauliflower 1. Cauliflower florets* (275g) 2. Breadcrumbs (50g) 3. Aioli (40g) 4. Ketchup (12.5g) 5. Chili flakes (a few pinches) 6. Salt (a single, cheeky pinch) I recommend measuring the aioli and ketchup together (in a large bowl) as you’re going to be mixing them anyway. * I’ve found that a half of a large cauliflower tends to be enough. The size of the florets is up to you, but it’s best to be on the side of larger florets. It’s 275g of florets, not the entire cauliflower. ## Garlic rice 1. Jasmin rice (150g) 2. Garlic (2 cloves, crushed or chopped) 3. Water (300ml) 4. Butter 5. Olive oil 6. Salt (a single, cheeky pinch) Maybe also have another 100ml of water on hand separately. Just in case. ## Bizarrely delicious mayonaise Apparently this is called “Bang Bang Mayo”, but I guess you can name it whatever you’d like to impress your guests. 1. Mayonaise (25g) 2. Soy sauce* (25ml) 3. Ketchup (12.5g) 4. Sesame seeds (10g) 5. Chili flakes (like, six pinches maybe?) I also recommend measuring the mayonaise, soy sauce, ketchup, and sesame seeds together (in a small bowl) as you’re going to mix these together to make the bizarrely delicious mayonaise. * Light or dark soy sauce, it’s your choice. I recommend dark. ## Wok vegetables If you don’t have a wok, then a small frying pan works fine. 1. Carrot, peeled & chopped into half moons (1 medium sized stick) 2. Spinach, roughly chopped up* (60g) 3. The white parts of spring onions** (the type Hatsune Miku holds) (2 sticks) 4. Olive oil Hot tip: Wash the spring onions as well as you can with cold water. * If you can only find baby spinach, then grab that. ** Prepare these by chopping off the end at the root (the hairy part) and then peeling off a single layer of the onion. Also trim the green tips so they’re pretty. Chop these from the root towards the green tips, horizontally so you get nice circular discs of spring onion. The white parts fry up better, and we’ll save the green parts for garnish, so try to separate into a pile of white parts that look ideal for frying (I find it’s usually the bottom 75% of a stick). ## Cucumber salad These won’t be cooked, so make sure you wash the ingredients with cold water. 1. Cucumber chopped into circles (1 stick for two people) 2. The green parts of spring onions* (2 sticks) If you’re not a cucumber type of person then a red pepper makes a good substitute. Half a red pepper cut into sticks. * See above for prep guidance (I find it’s usually the top 25% of a stick). # Additional equipment 1. Baking paper/parchment (1 sheet for a baking tray) # Cooking ## The krispig blomkål Pre-heat your oven to 200°C (if it already is pre-heated, then good job). Mix the **aioli** , **ketchup** (12.5g), **chili flakes** , and **salt** in a large bowl - I find using a teaspoon to mix is effective, and you can use another teaspoon to scrape sauce off the one you used to mix. Make sure it is combined well. We’ll be using this goo stuff to bread the cauliflower florets. Add the **cauliflower florets** into the large bowl, and carefully mix with a non-metalic spoon (preferably plastic, but a wooden spoon works). Keep mixing until the sauce has practically disappeared from the bottom of the bowl. Add the **breadcrumbs** a part at a time, mixing the cauliflower each time to get it all covered in breadcrumbs. Don’t worry if it’s not entirely covered, just makes sure you end up with all the breadcrumbs in the bowl, and a good amount on the cauliflower. Line your baking tray with baking paper/parchment, and then evenly spread the breaded cauliflower florets over the paper’d tray. Ideally the florets will bake evenly. Bake the breaded cauliflower in your preheated oven for 25 minutes (set a timer!). When baked: Remove from oven. If you don’t have enough work space to remove it from the oven, then turn the oven off (but do not leave it in the cooling oven for more than 5 minutes!). ## The garlic rice Melt a small chunk of **butter** , with a splash of **olive oil** , in a medium sized saucepan **ON A LOW HEAT**. If your butter turns brown (caramelises); you done goof’d. Throw in your crushed/chopped **garlic** , and let it fry (still on low) for 1 minute 30 seconds. If your garlic turns black (burns); you done goof’d. Immediately after the 1 minute 30 seconds throw in the **rice** and **water** (300ml) together, then add a pinch of salt. Whack that up to _full heat_ , we’re going to bring it to a (very) brief boil. When you see bubbles through the water, or hear the familiar sound of boiling, give the rice a brief stir with a non-metalic spoon (the sound of a metal spoon scraping on a saucepan… ugh), put a lid on the saucepan, and then turn the heat _back down_ to medium*. Cook on medium for 12 minutes (set a timer!), and **do not open the lid**. When that timer goes off, step away from whatever you were doing (hopefully the next step of this recipe) and turn the heat to the _lowest_ setting. If you’re concerned about the rice, quickly take off the lid and give it a single stir to check that not all the water has boiled off. If it has: Add that emergency 100ml of cold water, and then immediately put the lid back on. Cook on lowest for 10 minutes (set a timer!). The goal here is to steam the rice so it’s all fluffy. When THAT timer goes off, take the rice off the heat entirely (move it to a cold part of the hob). * If you don’t know what medium heat is on your hob, then go for a notch over the half-way point. ## The bizarrely delicious mayonaise In a small bowl, mix up the **mayonaise** , **soy sauce** , **ketchup** , **sesame seeds** , and **chili flakes**. Use a teaspoon for high precision mixing, and a second teaspoon to scrape the sauce off the first one. That’s it. Put it to the side. ## Wok vegetables In a wok (or small frying pan), heat up a splash of **olive oil** (medium heat should be fine), and add in the half-moon’ed **carrots**. Carrots take a while to cook, so give it 5 minutes. Add in the **spinach** and **spring onion whites** together, and cook for another 5 minutes. # Serving Get your bowls. The water should be boiled off from the rice, so if you’re British you can put down that sieve, and instead grab a spoon suitable for scooping out rice (a deep plastic one, or a wooden rice spoon if you’re posh). Divide the rice into the bottom of each bowl. Divide the breaded cauliflower into each bowl (on top of the rice). Divide the wok vegetables into each bowl (to one side, if you want a nice presentation). On the side of the bowl without the wok vegetables, put the **cucumber** slices (you can make them lean on each other for a nice presentation). Pour over the bizarrely delicious mayonaise in a nice zig-zag pattern. You want to get a nice splash on the wok vegetables, cucumber slices, and the breaded cauliflower. Make sure you divide this equally between the bowls, don’t dump it all on the first bowl… Sprinkle over the **spring onion greens** as a garnish. If the bizarrely delicious mayonaise isn’t enough for you, then siracha mayo goes quite well with this dish also. # Drinks to pair with Lager beers go well with this (pilsners, etc), but a pale ale works too (IPA).
felixjones.co.uk
February 20, 2023 at 12:00 AM
What does Felix do at Mojang?
I notice that content creators are somewhat forced to make guesses as to what the job titles & responsibilites are for various Mojang employees, and that’s probably because we don’t really show much of what goes on in the day-to-day for a Minecraft dev. It is probably easier for us to individually talk about our own jobs, and my job is actually something I talk about a lot when I give talks to universities about working in the games industry. As usual: All my opinions expressed here are my own, they are not representative of Mojang Studios, nor the Minecraft team. I’m just Felix the game developer talking about his job. * * * # My Job Title Probably the most official _public_ title would be the game credits itself, where I am under the “Game Developer” heading: Someone even made an empty Moby Games page for my Minecraft credit! ## A YouTube job title!? Whenever I’ve been involved in videos on the Minecraft YouTube channel someone from the production team will send me a message asking what job title should go on under my name. Let’s take a trip down memory lane of my Minecraft YouTube appearances. Holy heck just noticed my Twitter handle is in this appearance, pretty sure I could have used that as evidence criteria for getting verified on Twitter (back when being verified actually had meaning). Fun fact about this video: We were all working from home, and I happened to be trapped in the UK during the first of the pandemic lock-downs. Behind me on that white wall were studs used for hanging pictures; I had to keep my head and my hands as much out of the way of those studs so they could be digitally removed in post. My "children's TV entertainer" costume. My favourite part of this video is when I preemptively clap and smile at Paulo’s suggestion of adding beans, because it was the 2nd take of him saying that so I knew exactly what was coming up. It either delivered as some kind of crazy awesome synergy between us, or me just being far too enthusiastic about being on camera. This is me counting how many diamonds I mined since Caves & Cliffs Part 2. In this video I begin my answer with an excellent joke that I had originally said prior to the shoot, so I decided to repeat it again on camera—and everyone put on a convincing laugh as if it was the first time they heard the joke. That was the moment I realised just how good my colleagues are in front of the camera, it’s probably a skill we take for granted. I was very impressed, and I still think about that moment. # What job did I sign up for? On my contract I am a “Java Game Developer”, so that’s probably why I gave that as my job title for the YouTube videos above. Actually now that I’m looking at my contract again, there’s two responsibilities written on it that I will paraphrase (I won’t show my actual contract): * Design and implement gameplay systems * Improve stability and performance So I guess that’s what I do at Mojang? These are actually more vague than one might think; generally any game developer is expected to implement “systems” (gameplay, or otherwise), is expected to fix bugs (make the game more stable), and the word “performance” can be interpreted to mean many, many more things than just _frames per second_. # What I _actually_ do at Mojang Studios As of this writing, my official responsibilities are all Minecraft Java Edition—which basically means “stuff” that is specific to Java Edition, and has little-to-no relevance to Bedrock Edition (so not the gameplay update content stuff, more Java Edition engine things and features). That being said, I’ve noticed that many of us on the Minecraft team have many more responsibilities than you’d typically expect from a game developer. I even mentioned it here. What surprises my friends and family when I tell them about my job is how close to the community we are. They are surprised at the level of interaction I have, and seem to view me as doing the job of marketing. Our personal social media accounts are definitely **not** Minecraft marketing. To be honest, I actually use my twitter to generate Minecraft news for ItzJhief, and come up with new Minecraft jokes for Phoenix SC to steal ## The one responsibility that surprises people the most From my experience; the most common thing that surprises Minecraft fans is that Java developers themselves write the snapshot posts and changelogs. I remember a new hire being surprised by this, and I think I remember IBXToyCat being surprised by this during conversation over lunch that one time 🤔 We have a writing team and a community team that gets involved and helps out, but when it comes to technical stuff the best people to write it are probably the developers themselves. The team also produces the screenshots for the blog posts, which can sometimes turn into a competition of who can create the best screenshot. I’m actually quite proud of some of our blog-post screenshots. Gegy and I worked on this one, which involved some command trickery in multiplayer. I’m pretty fond of the “Minecraft animal staring back at you” vibe we got going on here. ## “Don’t blame Xilefian, he isn’t responsible for this” I once saw a comment like this in a thread regarding something that was broken in a snapshot, which made me laugh because I was actually responsible for the thing. It is impossible for players to know what part of the game they can attribute to who, and IMO that’s probably a good thing as the responsibility of the game should be shared across all Minecraft developers. That being said, there are some things in the game that when I look at I think “That is soooo Cory”, and checking the code I will see Cory’s fingerprints all over the history. I do find YouTube videos that attribute the entirety of a Minecraft update to the beloved Minecraft modder: kingbdogz. It could be an interesting thought experiment to try and figure out what a Minecraft update would look like if it was indeed dictated by a single designer. # My Next Job Title Someone thought it would be a good idea to make me even more responsible for _the best selling video game_ than I already am, so starting early December my job title will no longer be “Java Game Developer”. I’ll probably write another post revealing what my next Minecraft job title is when the time comes (there’s a few funny stories related to that). * * * This was a bit of a random post, but the job titles we have is something I think about a lot when it comes to community interactions, and with my job title changing soon it has been on my mind a fair bit.
felixjones.co.uk
November 17, 2022 at 12:00 AM
Website Update: Now you can leave a Comment
With the future of Twitter in uncertain hands I decided I should probably have a back-up plan for discussions around my website content. * * * I mentioned back in July 2022 that I’d enable Discus comments where it made sense, but with Discus being rather evil I instead started telling people to @ me on Twitter (@Xilefian) with their thoughts. For myself, Twitter is great for visibility. Even before I had the _fantastically cool_ clout of Minecraft-dev, my Twitter sphere was quite healthy in the communities around graphics programming and game development. For the lucky few like myself, Twitter has been invaluable for learning and communicating. Thanks Twitter. # What’s next after Twitter? If Twitter had a mass-exodus (digg style) I wouldn’t be surprised if those who have the priviledge of high twitter-visibility go back to individual blogs with RSS. This somewhat goes back to the days of Web 1.0 where everyone had a personal website. ## Mastodon looks interesting So Twitter is kind of about you just blasting out whatever personal crap you have going on, whereas Mastodon’s servers promote a most focused blasting out of very-specific crap that you have going on. If you want the personal blog style, you need to run your own server, or start spewing all over someone else’s server that was set up for twitter-style top-of-mind discussions. So perhaps the form I imagine of “individual blogs with RSS” is individual Mastodon servers. There’s a technical barrier that’s probably greater than just rolling your own website (which is already a high technical barrier). ## Using GitHub (lol?) as a social platform The other day I had a chat with Chi regarding the Twitter “news” and I mentioned that I am considering looking into using GitHub as a sort of stupid twitter-clone - and that idea actually stuck with me. In terms of visibility, I think I’m doing pretty well on GitHub: As of this writing I have 209 followers on GitHub (give me a follow). The more I think about it, the more it makes sense to use GitHub for topic-based social discussion (as long as your topic is related to software development). My second biggest crowd after Minecraft is probably the game development scene, so why not use GitHub. It would be interesting if GitHub set up a feed where you can see the top-of mind from individuals that you follow. Perhaps this already exists? Right now I just see a feed of all their boring, normal software development activity (make a repo, push a commit, merge a PR, etc). # Giscus blog commenting This morning when I was making my coffee I was reading about all the Twitter employees who were unfortunately fired in the wake of the “news” and it got me thinking even more about my GitHub social discussion idea. I decided I’d spend this weekend building a sort of comment system using GitHub discussions, so I booted up my development machine and began the way all good software starts: by google’ing for whatever already exists. Even with my garbage tier search terms I immediately found that someone had already done what I had planned and it is quite glorious: giscus. The name alone communicated that it was what I wanted for replacing discus on this site, and setting up giscus was ridiculously easy: it worked first time. So I went ahead and enabled it on every page. I hope you have a GitHub account. # What’s next for the site? I am thinking of experimenting with some kind of top-of-mind style tweet-esque thing on the homepage of this site, so my personal top-of-mind stuff would be visible for discussion, and I can share cool games, projects, communities, political thoughts, and more. But for now, Twitter is still here, so I’m sticking to Twitter for that kind of thing. This blog will do for continuing my long-form content, and the comment box will do for continuing discussion in a place that isn’t dependant on the existance of Twitter (but instead dependant on Microsoft’s seemingly infinite free storage on GitHub). I should probably set up RSS. ## When will you add dark-mode!? Yeah I need to do this at some point. I’ll look into it when I got some time, but I need to read up if there’s any data laws I need to keep in mind when I write a boolean flag to your disk for if you have dark mode turned on/off. Perhaps there’s an OS/browser setting I can use? # Leave a comment! Below here should be the new comment section. I’ve even left GitHub reactions enabled so you can drop an emoji that best describes your emotional state when reading the crap I write.
felixjones.co.uk
November 5, 2022 at 12:00 AM
Mob Vote post-mortem
First of all: All my opinions expressed here are my own, they are not representative of Mojang Studios, nor the Minecraft team. It’s just a small sliver of my thoughts (my colleagues certainly hear the meat of what I have to say). To prove this isn’t representative of the Minecraft team, I (Xilefian) will type the F-word: Fuck. Mojang Studios would never write that in any official statement, so you’d better believe it’s just Felix’s conversational ramblings. My colleagues have suffered through the full wrath of my feedback, this post is just a small taste of what they get to hear from me. For purposes of full transparency: I do not have any direct involvement in Minecraft Live, nor the mob votes, so I am likely talking out of ignorance regarding how things work and my opinions are probably going to be garbage as a result of that ¯\\_(ツ)_/¯ Every year for Minecraft Live there is the Mob Vote, where the community votes for a mob to be added to the next Minecraft update from three candidate mobs. # Minecraft Live 2020 The mob vote for Minecraft Live 2020 was between three mobs: Iceologer, Moobloom, and Glow Squid. The favourite amongst the technical Minecraft community was the Iceologer, and quite frankly I expected the Iceologer to win because the technical Minecraft agreed that it was the obvious choice, and their audience agreed and carried this wisdom onwards when campaigning for their mob vote favourite. Meanwhile, a rather famous Minecraft content creator made a joke tweet stating that they will follow (on twitter) everyone who can prove they voted for the Glow Squid - providing some incentive for their many, many fans to vote for their favourite mob. They did retract this offer, but many see this as the damage being already done. The Glow Squid won. ## Was the vote really rigged? The Glow Squid win was a real bloody surprise to myself, as well as the technical Minecraft community. The sentiment within my circles was that it was a meme vote, and undeserving of its victory. Immediately I will say: I do not believe that any single creator swung the 2020 mob vote. I had this opinion at the time, and I still hold that opinion. The first thing I did was ask my younger niece and nephew which mob did they vote for. They both said “The Glow Squid, of course” (as if it was the obvious choice). These are young Minecraft fans who had no idea who the content creator that “rigged the vote” was. I then started looking at what parents were saying about which mob their child picked, and again I was seeing a lot of support for the Glow Squid. So I am convinced that what happened was our familiar Minecraft content communities created a conversation bubble where they put forward some indepth thought and discussion regarding why the Iceologer was a good idea, their audience heard these good thoughts and discussions, agreed, and then spread the converstaion as far as the familiar Minecraft content communities could reach. But these communities probably don’t have the reach that we imagine. They probably aren’t in the school playgrounds where many Minecraft discussions are taking place. ### But Minecraft isn’t just played by kids Yes, Minecraft is played by everyone, including adults. Adults who may not have the time to keep up with Minecraft news, who may not be able to take time out of their day to vote for a Minecraft mob, or time to read up about any of the Minecraft Live news. After every Minecraft Live I check the conversation on Reddit, and I see the same pattern: A discussion about an announced feature, with many people in the comments saying “what is this?” and “was there some kind of announcement?”. I imagine that’s the normal Minecraft player: They have no idea a mob vote is even happening. ## Why the Glow Squid? This is very anecdotal, but when I looked into why indiviudals wanted the Glow Squid I did indeed see comments about how the submarine in the video was cool (the submarine was not added to Minecraft, and was never a planned idea), and also comments about how the Glow Squid will hypnotise players like in the video (the Glow Squid does not hypnotise players, and I have personally never seen plans to make the Glow Squid hypnotise players). I didn’t see many comments expecting the Glow Squid to act as a dynamic light, and I imagine this is because dynamic lighting isn’t a concept well understood by the kids who were so interested in the Glow Squid. Of note, I noticed the frustrations from the community were pivoted around “it doesn’t even glow!” - a sentence which has stuck with me. I passed this feedback to the team as a lesson to learn: There is a balance to be had between the media team exercising their creativity (which I 200% believe is something we should promote), and presenting good, correct information to the community. # Minecraft Live 2021 To me, 2021 felt far less chaotic and drama-filled than 2020 (or perhaps I hang out in the wrong places?). The Allay won the vote, beat the Glare and the Copper Golem. I had wanted the Copper Golem to win, I personally find Minecraft builds most charming when there’s a bit of life and activity to them (I tend to construct useless piston and light contraptions behind the windows of my building façades, so it would look like someone was at home in them). ## The mob presentations were unbalanced When watching the mob videos, the utility of the mobs was boiled down to: * Thing that tells you where it’s dark (Glare) * Thing that pushes buttons, and then freezes still (Copper Golem) * Thing that you give an item for it to find, which will then pick up the same item from the ground when encountering said item, and it will then drop those items at a note block (Allay) I’m probably being a bit harsh, but to me you can tell that the Allay was a mob that had already been quite fleshed out and developed versus the other two. Anyhow, the technical Minecraft community saw the utility in the Allay, so the discussion around the Allay winning was mostly positive. I remember sarcastically presenting the 3 options to my colleagues in the office after the vote, where I went into grand detail about all the possibilities of the Allay versus the other two. I think there was a lesson to be learned, but I was too upset about the Copper Golem losing to communicate it properly at the time. ## Briefly: Why I liked the Copper Golem I remember when I was testing the performance of the Fabulous graphics shaders on Java Edition I downloaded some futuristic city maps, and these amazing cities all felt like some kind of 28 Days Later apocolypse had occurred because they were so spookily empty. That was still fresh in my mind when I decided the Copper Golem was my choice. It’s a shame that we communicated the Copper Golem’s primary function as being a button-pushing drone. In my imagination I foresaw us one day expanding the Copper Golem functions to further promote the feeling of a base being alive. # Minecraft Live 2022 This year the mob vote was done differently, and Minecraft Live was prefaced with a blog post that this year the team will only show off things that are “ready to take the stage”, this meant that the team would only reveal features that are ready for players themselves to try out. The mob vote, by its nature, can’t follow that rule. If the mobs were developed to a stage where players could try them out, then there wouldn’t be a mob vote and all three mobs would be in the game (and we’d be missing two other features that otherwise would have been in the update). ## The mob presentations were unbalanced again I would have originally said the lesson to learn from 2021 was not make one mob clearly more awesome than the other, but after this year I’d revise it to: don’t pack one mob with more information than any other. * Thing that hides in caves and gives you loot when found (Rascal) * Thing that holds items and walks back to where you placed it (Tuff Golem) * Thing that has ancient mystery, hatched from ancient eggs that can be found underwater, can be bred to revive the ancient lost mob, sniffs out plants to dig up ancient seeds, ancient seeds that you can grow into a new plant, and it can also grow to be really big (The Sniffer) Again, I’m probably being a bit harsh (because I wanted the Rascal to win), but I was already bracing for a Sniffer victory once all three videos were released because it felt like another case of one obvious mob. > The sniffer received more than half of your votes! 🤯 > > Mob Vote 2022 results: > Sniffer - 55.1% > Rascal - 27.7% > Tuff golem - 17.2%#MinecraftLive > > — Minecraft (@Minecraft) October 15, 2022 ## It was all in the details! One frustration I saw repeated was that details for the Tuff Golem and Rascal were hidden on the blog posts on the websites, which are an extra click away from the easy-to-consume videos, and many Tuff Golem voters specifically wrote to me saying “I wasn’t going to vote Tuff Golem until I read the blog post”. # Why have mob votes to begin with? When I first joined Mojang back in 2020 this question was raised before Minecraft Live, and the answer I heard was quite wonderful. I can’t quote it exactly, but the gist was: It is a way for us to have all Minecraft players have a say in the game’s development, not just the players who we interact with every day in the community. I really like that, it plays into my discovery about the Glow Squid being a favourite amongst the Minecraft community we don’t have much visibility over (the Minecraft communities that occur in a school playground), and I definitely believe the mob votes give us more than it does take us away (for one, it’s an anticipated part of Minecraft Live itself, and thus draws a big crowd). # Why not add all three mobs? I was surprised by this idea being repeated in the community, because I had thought (from my priviledged position) that the answer would have been obvious. Ignoring the fact that this removes the mob vote itself (which would be a shame), adding all three mobs is in effect adding none of the mobs, because they would be folded into the regular update features and would disappear from the community. It would probably no longer even be a mob, it would just be another slot for another feature, be it a block, biome, item, mob, whatever. And given that time isn’t infinite, the three mobs would be cut back down to one mob anyway. If we had the bandwidth to add all three mobs, that bandwidth is already being used for other parts of the update. ## But seeing the other two mobs lose makes me upset I did see an interesting comment on this, where a player was specifically upset because we have shown them mobs that they want in the game, but they won’t be in the game. This was the elusive “Mojang promised us XYZ” sentiment that I had been looking for, finally found it in 2022: this was a player who sees any Minecraft content at all to be a promise, and the mob vote to them was a vote for which two promises will we break. Very interesting, and I’m not sure how to navigate that one. The first thing that came to my mind was “wow buddy, you’d hate to spend a day at Mojang”. We discuss ideas constantly, just walking through the hallway, whilst grabbing a coffee, when trying to put out fires that we accidentally caused in the microwave, during all these normal office moments we talk about ideas. Sometimes it’s mob ideas that we experimented with years ago, sometimes it’s new ideas, sometimes it’s ideas that were cut, sometimes it’s future ideas that we aren’t sure will make it. If all of those ideas were promises being broken, then this individual would have a horrible time just watching their favourite game be developed. To me, the mob vote is voting for one of those ideas to not be trapped as a lost echo in the hallways of Mojang Studios. I’m probably not communicating my point here very well, probably because I need more time to think on it. ## But modders can add these mobs real fast I almost forgot to address this, but I really should. Modders have a few benefits on their side: ### The mobs have already been designed A lot of the work done is in the design phase, once that’s done developers then start picking up tasks that are needed in terms of priority. Modders can skip the design phase (because we already did it), don’t need to worry about the current work priority (because they don’t work at Mojang) and can get on with it on their own time. ### Modders only work in Java Edition I’m not sure the community can really understand how much working on two different games with the goal of feature parity slows things down. A Java technique almost never translates into something usable in Bedrock. Quite frequently we hit into problems where we’ve ran ahead with a solution in Java Edition (where we pretend we have infinite memory), only to be hit by the realities of needing to get the idea working on platforms without infinite memory for Bedrock. Modders are incredibly lucky that they just need to worry about Java Edition. ### Modders probably aren’t working in an organisational structure It’s not just working on the game, it’s working on the franchise. Minecraft has a LOT going on outside of just features in the game, and employees at Mojang Studios are uniquely responsible for quite a lot of stuff that would shock and surprise anyone expecting a typical software/game company setup. Part of that structure includes communication through code. There are a LOT of people touching the code at the same time, modders tend to be working on a codebase that’s set at a particular level of Minecraft (almost never a latest snapshot) and probably don’t have a pull-request workflow that has 20+ people staring at it. ### Mods don’t need to get it right the first time I have noticed that many mods showcased for impressive speed of development versus Mojang have the label “beta” on them. A modder can release a mod, and then work on the bugs and fixes for post-release patches. Of course, we fix bugs post-release, but these slow down development of other features that need priority, so it’s better to get it right the first time, and that takes time. ### Modders have an incentive to make the mob vote mods first Adding them as fast as possible before anyone else (including Mojang) is a good way to get downloads of your mod (or get players to join your server where you host these mods). This is why the modding community is so great, it drives itself to innovate at a pace that cannot be matched by a slow organisation. Incentives like “we did it before Mojang” promotes modding and is fantastic. Around the time of the mob votes, I see so much community activity around player created content, including mods. # How to fix the mob vote: By Felix Again, this is my personal opinion, and when I presented this to some of my colleagues they were quite shocked, confused, and frightened, because this idea is a little crazy. My crazy idea would be to say practically nothing about the mobs. We show off their design, we show off the environment it is in, and we say if it is a passive mob or a hostile mob, and that’s it. The players then vote on the mob that gives them the best vibes. We then go into round 2 of voting: What should this mob do? Does it drop a rare item? Does it give a new block? Can you ride it? And then we go into the final round of voting: What should the mob be called? It’s an extreme, complex, and cumbersome approach, but I think this would end any kind of appearance of imbalance where one mob is an obvious vote versus the others. My favourite part of this is that it moves the design ambiguity back towards our game designers, who would now be given far more room to flesh out the mob after the fact of the vote (instead of revealing all our cards at the beginning). I will flesh out this idea a bit more to try and find the flaws in it - and more importantly figure out if it is solving anything worth while, because another thing I realised: # The Mob vote is fine Yeah after Minecraft Live 2022, I kind of felt like I didn’t need to write this huge post. The mob vote is actually fine, it’s harmless, it’s just a game, and it’s a game that will go on for a long time, any mistakes made can be fixed in an update. So perhaps we don’t need to fix or change anything. That’s 2% of my thoughts on mob votes. If you want the other 98%, then you need to join Mojang, get your NDA signed, and ask me in person. If you disagree with me: then @ me on twitter. Opinions are welcome and I tend to forward interesting points to the rest of the Minecraft team.
felixjones.co.uk
October 16, 2022 at 12:00 AM
Resident Evil’s tank controls are good
I am once again here to make the case that the thing everyone says is bad, is actually good. # The problem. Take a look at the original PlayStation controller: (Image by the awesome Evan Amos) Notice that this controller does not have analogue inputs. The player is forced to move only in the 8 directions allowed by the directional-pad on the left. How does the player move at angles between those 8 directions? Tank controls to the rescue: By instead using left/right buttons to spin the player, the player can now walk forwards/backwards at all those angles within the 8 directions. # The other problem. The Resident Evil HD Remaster has an option to remove tank controls, in favour of the finer analogue input that can reach all those angles between the 8-directions. This is great for the modern gamer, who is trained to expect “up” to mean “somewhere in the direction towards the top of the screen”, but it immediately exposed the 2nd problem that tank controls solved. Resident Evil has multiple fixed camera angles of the same room, and as the player reaches the edge of one angle the other camera angle is swapped in. If you were holding “left” to run towards the “left” edge of the screen, but the new camera angle swaps to the other side of the player, then within the world the “left” edge of the screen is now on the “right”, so if you were still holding “left” you’d now be walking the other direction, **back towards the edge of the screen you came from**. This results in the player accidentally flipping between two screens if they weren’t quick enough to release the movement button. ## The Final Fantasy solution The PSX era Final Fantasy games (which also use fixed camera angles in a 3D world) solves this by: 1. Making the location the player loads into be a little ways away from the edge of the screen 2. Having larger screens that pan, instead of changing camera angle, requiring a loading screen between areas Both of these give the player plenty of time to let go of the movement key and re-orient themselves. ## The Resident Evil HD Remaster solution When you use the option to remove tank controls, the HD Remaster will detect when the player is holding the analogue stick between camera angle changes, if this is the case then on the new camera angle instead of resetting where “up” means relative to the new camera angle, the previous camera angle’s “up” is kept relative to the world. I’ll try to explain that better: When you press “up” you define a new “north” within the world, this “north” is kept between camera angle changes until you let go of the analogue stick. # Okay so tank controls were necessary, but they still suck No. I see a few comments every now and then from players who say the tank controls added tension to the horror. I do think they are assigning too much design thought behind the tank controls (IMO they are the solution for fine movement with digital inputs), but I do get what they’re saying, it was an emergent part of the Resident Evil experience, attempting to back up from a zombie that got too close, only to trigger a jump scare behind you that you now need to sprint forward to avoid, running around the first zombie. This is probably felt best in Resident Evil 4, which is a third-person “over the shoulder” game. It was the last Resident Evil game before Capcom went down the weird path of making them action games. I feel the availability of strafing is what contributed to the move away from survival horror, and towards action games. With that player sentiment towards tank controls, I feel there is something to tank controls that’s worth looking into. # Other games with tank controls Indeed, there are games that have tank controls that people seem to forget. The id games Wolfenstein 3D, Doom, and Quake use tank controls, with the modifier key to convert the turn directions to strafe directions. I wouldn’t be surprised if the id game Hovertank 3D used tank controls because it’s a literal tank (and that it was made a “hover” tank because of the strafe modifier). These games were released for DOS, where the mouse was optional, and all player input was through the digital keys of a computer keyboard. Continuing the FPS trend, GoldenEye 64 uses tank controls. I don’t think any of us complained whilst playing 4 player split-screen back in the day - but then again, we didn’t have anything better to compare against (other than mouse & keyboard FPS games). ## A special FPS game Resident Evil Survivor. It’s a game I feel I need to defend by itself, but I’ll leave that for another rambly blog post. As with all PSX games, the game assumes the bareminum controls of the original PlayStation controller, with its digital old directions. This game uses an FPS control scheme similar to what GoldenEye 64 uses, with tank controls and a dedicate “aim” button for moving the reticule, only with RE Survivor the _only_ way to shoot the gun is to first aim. After re-playing this game a number of times over the many years (it is very replayable) I had the epiphamy: The controls of this game are the **same** as the mainline Resident Evil games! Tank controls, hold button to aim/activate shooting, direction pad is used to adjust aiming (turning and aiming up/down). # An extra rant about PSX vs PS1: Yes Sony made a console called the PSX which was a PS2. But we all called the PS1 the PSX back in the day. I have a theory that this comes from the codename for the original Nintendo PlayStation, which has a chip with the markings “SFX” (not to be confused with the Super FX chip). When Sony moved onto the PlayStation they probably just replaced the Super Famicom with PlayStation to make PSX. Maybe the X means CD ROM eXpansion???. * * * Alright, that’s the end of my ranting for today.
felixjones.co.uk
September 22, 2022 at 12:00 AM
That one time I made a Minecraft mod
# That one time I made a Minecraft mod I remember being quite impressed by the ComputerCraft mod (which is apparently open source now?), and it inspired me to attempt my first Minecraft mod that basically ripped off ComputerCraft and just did things the way past-Felix thought things should be done. Whilst I was very familiar with Lua at the time, the first programming language I had begun to toy with was Ruby, and I had recently figured out how to embed CRuby (the full-fat Ruby interpreter, mruby wasn’t around at the time), so I thought I’d be really clever and make a mod that adds a “Ruby Block” to Minecraft, which would literally be a block of Ruby that executed Ruby script. I still think Ruby would thematically be a hilarious choice for a Minecraft script language (but if you were to ask me with my sensible hat on, I’d give a different answer these days). I love the little pun of “code-block” crossing with “Minecraft block” to make it a literal block of Ruby code. Rubies have a bit of history in Minecraft (does RubyDung count?) where they were going to be the original currency for villager trading (according to the unofficial Minecraft Wiki), so I guess this was me attempting to revive them without the ore block, but as something totally unexpected — in hindsight this is the type of out-of-the-box interpretation that tends to be most appreciated by my colleagues in Mojang Studios when it comes to Minecraft ideas, “Rubies aren’t an ore or gem, they’re something totally different”. ## What went well? I don’t have a good memory of what the mod making process was like back in the day, but my grasp of Java 8 was good from my experience working with Android, and I remember the tutorial I followed “how to add a block” got me to the point of having a block of “Ruby”, from there it was very easy to get JNI interfacing with my Ruby DLL and have a little “Hello World” activating, printing in the chat too. I think I had it run every tick? Didn’t get around to setting it up to activate on redstone. I also got Ruby snippets working in the chat, so anything written in chat would be executed as Ruby. I had ideas of a scripting console (Quake style drop-down!) so one could script stuff in the game with Ruby. ## What didn’t go well? I identified a big flaw in what I was doing: I’d need to ship compiled binaries for my mod, and I only really had the means to debug a Windows DLL at the time, so immediately Mac and Linux fans would be missing out on my genius Ruby block. ## Why didn’t I finish it? The week after I began development, Mojang announces the Command Block. At the time it appeared to me that the Command Block covered a significant chunk of use cases I had in mind for my Ruby block, and the rest of the use cases the Command Block didn’t cover was already achieved in ComputerCraft, so I didn’t have anything that would be impressive enough on its own, at most I was replacing Lua with an even less popular language. The Command Block stomping on my idea stuck with me long enough that I stood in line at the Command Block panel of Minecon 2015 amongst a load of farting kids to ask the panelists for their thoughts on what would have been different if Command Blocks ran a full scripting language like Lua. I never asked the question because all the time was taken up by the kids in front of me asking “What’s your favourite block” multiple times over. ## Learnings I revisitted the Ruby powered Quake console part of this mod a number of times in later projects. From my experience of typing crude Ruby into Minecraft chat, I had realised that Ruby was oddly awesome for game commands. A lot of the syntax felt like traditional game console commands, rather than typing out a program. I wrote an article about my revelations regarding Ruby syntax for game commands, and reading that back (it’s cringe, please don’t read it) makes me still think Ruby would have been a great choice for game scripting. This is one example from that page for teleporting the player if they are an admin: Position.set x: 100, y: 110, z: 40 if Player.access == :admin If I were to imagine how this would have looked if I finished my Ruby block mod it would probably be: Player.teleport x: 100, y: 110, z: 40 if Player.team == "admin" That’s just one idea. You could probably get the syntax into something looking really cool and easy to read for a non-programmer. ## Would I revisit making this mod? Probably not. I’m not even convinced Minecraft needs Command Blocks, let alone a full programming language in a block. ComputerCraft has that covered. As for a proper scripting environment, I don’t think Ruby is the right choice: Lua would make more sense because of its prevelance, but the game has such a huge educational power that it would be a missed opportunity to not go for a language that provides a transferable skill.
felixjones.co.uk
August 5, 2022 at 12:00 AM
CMake Doesn’t Suck
I found a draft of an article I started writing on Medium titled “Enjoying CMake”. I don’t think enough people defend CMake. There’s a reason it’s ubiquitous in the C++ ecosystem. There’s some people out there who think we should all migrate to Meson, but I simply don’t see a need. CMake honestly does the job, and Meson to me looks like a project born out of frustration that no-one is willing to talk about CMake’s weird as heck syntax. Plus Meson has a Python requirement and that puts me off immediately. # I actually like CMake I like that CMake is actually updated and actively worked on. The new versions tend to add stuff that I find quite useful. ## But the syntax is funky I don’t like the syntax, it’s really goofy. Having to write `endif()` and `endfunction()` is weird. Clearly it was written to be simple to parse, and I guess because of that it is accidentally okay to visually parse. Most of the time. There’s some legacy nonsense, like when do I `${put my variables in these dollar braces}`? When it comes to the `if()` command it can be inconsistent and surprising. But syntax isn’t everything, any decent programmer can learn a new programming language as long as they have documentation, which CMake certainly has. ## Returning from a function is weird Particularly trying to return anything at all from a function. function(do_stuff _result) # Do some complex stuff set(${_result} "Hello, world!" PARENT_SCOPE) endfunction() do_stuff(stuffResult) message(STATUS ${stuffResult}) So to “return” a value we have to hand a function the name of variable we want to store the value in, and then the function sets the variable of the parent scope from the inner scope. There is no `return()` keyword. Perhaps I find this weird because I’m used to C, which has a rather strict “can only return 1 thing” restriction that CMake avoids through this bizarre pattern. ## Parsing function arguments is kind of cool I have code like this: set(options VERBOSE ) set(oneValueArgs OPTIMIZE ) set(multiValueArgs INPUTS ) cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) # ARGS_VERBOSE, ARGS_OPTIMIZE, ARGS_INPUTS This is quite convenient, lets me write some pretty neat CMake APIs. ## Why is everything a function??? Everything ends in the call parens `()`. I’ve seen some crusty, old CMake that uses the closing keywords to add some documentation, but it doesn’t actually do anything, so now we’re all left with parenthesis after every key word. I got used to it, but it was annoying. ## What is the common coding style? I had to make up my own coding style for CMake. I wish the official documentation had some kind of recommendation. The style of old CMake is a bit shouty with EVERYTHING BEING UPPER CASE. # But I didn’t always like CMake My first introduction to CMake was Android. I had no idea what I was doing, I had to Google and copy-paste snippets to get anything done. I could barely figure out how to add header include directories. That was my CMake experience for a long, long time. # Writing CMake APIs I think everything changed when I first saw a self-configuring project on the Game Boy Advance, it wasn’t written in CMake, but I liked what it was doing and wanted to figure out a way to get a self-configuring toolchain going and available with familiar IDEs. CMake is available with familiar IDEs (Meson, is not) so the choice was obvious. I realised that I could provide GBA specific options to developers making GBA homebrew right there in the build system. Part of the build process involves running `objcopy` to copy up all the binary in an ELF file into a final GBA ROM binary. gba_target_objcopy(myTarget OUTPUT myGame.gba PAD 256 # Pad the binary to a multiple of 256 bytes FIX_HEADER # Apply the header checksum that makes the ROM work on hardware TITLE "Awesome Game" GAME_CODE "1AGE" # Game identifying code MAKE_CODE "FJ" # Made by Felix Jones VERSION 123 ARCHIVE_DOTCODE # Generate eReader dot-codes (lol) REGION usa # eReader region NAME "My Awesome Game" ) I am pretty proud of this setup. I think it looks cool, especially if you come from seeing traditional CMake projects which are a wall of `set()` commands with mysterious variable names. # My CMake complaints I think my CMake complaints would surprise people, as none of them are related to the language. It’s all about IDE support. Also, Meson has these same problems. ## Tools don’t auto-complete CMake code I like auto fill suggestions popping up as I type stuff. That would be awesome for CMake. I have all these cool APIs that people can’t discover because no IDE has auto complete for CMake. In most IDEs I can Ctrl click on something and be taken to its definition, but IDEs seem to not implement this for CMake. ## Doxygen for CMake code? Forget it. CMake has support for Doxygen, but Doxygen doesn’t support producing documentation for build systems. This really sucks, as I struggle to document and communicate to developers what is available. ## It’s still WIP?? Here’s a complaint that isn’t tool related: There are CMake features that are still half-finished. One common action in GBA projects is to bundle game assets in a GBFS archive and append it to the end of your game. In my CMake toolchain it looks like this: gba_add_gbfs(myAssets GENERATE_ASM "hello.txt" "file.txt" "image.bin" "things.json" ) # Add the assembly output of myAssets as a source file for myGame add_executable(myGame main.c "$<TARGET_PROPERTY:assets,ASM_OUTPUT>") That `ASM_OUTPUT` is hard coded to `myAssets.s`, which is rather inflexible if you need to rename the source file to something else (changing its extension, even). I explain why it’s like this in the comment: # Ideally this would use $<TARGET_FILE_NAME:${_target}> # However, generator expressions are not supported in BYPRODUCTS for add_custom_command # https://gitlab.kitware.com/cmake/cmake/-/issues/12877 Issue created 10 years ago, it has been partially solved (and actually looks like there’s some activity related to it in other issues. Hmmm maybe I should see if this is solved now). It surprises me that there’s still problems like this. # Where the feck are examples of GOOD CMake projects? They don’t exist. There’s things I want to do in CMake that apparently no-one has even tried, and whilst the documentation does its best to describe what’s available, something I want to see examples to get inspired. Quite often I ended up digging in the source code of CMake itself to figure out what’s available and possible with some CMake functions. My theory: Nobody is proud of their build setups, and why should they be? All the cool stuff is in the thing you’re building, not how you build it. And because nobody is proud of their build setups, they let them rot into embarressing messes, and then someone else takes a look and says “gee, CMake sucks doesn’t it?”. ## A CMake horror story More than once I have seen this setup: * Python script generates CMake project * CMake project generates IDE project This is so dumb to me. Clearly what has happened was: “Ugh I hate writing CMake, it is so unreadable and I don’t want to learn the goofy as fuck syntax. I will just write my own Python script that performs all the tasks we need, everyone will enjoy reading my Python code and debugging that”. CMake has a scripting language: it is CMake. You don’t need Python in your build. That’s 1 less dependency and 1 less barrier of entry to getting people started on your project. I am still in disbelief that I have encountered multiple projects with this Python dependant setup. Madness. Oh and of course they use `FILE(GLOB MY_SRCS dir/*)` and scan for sources, because actively making the CMake experience worse gives you more things to complain about. * * * In reading this back, I mentioned Python more than once. I have nothing against Python, but too often I see it thrown into a project as some kind of silver bullet for bodging around something that could have quite trivially been solved with the tools already at hand. Anyway, in conclusion; I’d like people to stop hating on CMake. Thanks.
felixjones.co.uk
July 30, 2022 at 12:00 AM