Fast Leaf Decay for Minecraft: Bedrock Edition
I'm not an old timer when it comes to Minecraft, but I have been playing since 2020. I started on the Java edition but I prefer Bedrock edition. I have multiple devices, I like to play on the same worlds, and I use realms to make that happen.
One of the areas where the Bedrock edition is usually called out is in the world of mods (called add-ons for Bedrock). There just aren't as many, and they are much more limited. There are many reasons for this and it has been steadily improving over time. But there is a long way to go.
One of the main add-ons I've wanted as I play on Java servers is fast leaf decay. It's a quality of life add-on that doesn't take away from many players experience. Why isn't it on Bedrock? Well it is now, because I've made it. It is called Spoiled Leaves. Here is my story of development to finally making it public just this past week.
The making of
I didn't know what it would take to make a script based add-on. The only one I made before just replaced the audio for villagers so it didn't require any programming. Instead of making a full plan, I started with creating a proof of concept so that I could see if I could do it with the scripting API.
For my IDE, I wanted to use NeoVim (and did) but also used bridge. (the period is part of the name) to make some of the process smoother. Since I was just making a proof of concept for my own education I could poke around without any time constraints. It was so much fun. It had been a long time since I had that much fun programming.
After a while, my work showed that it was doable, now I decided to try to make an actual add-on. All the other examples I saw were using experimental APIs, so I decided to start with those on then see if and when it became possible to use stable APIs. The examples were also in Javascript, and I hadn't setup a typescript compiler so I also started with it in javascript with the idea that I would convert to typescript after I got it working.
Recursion
The solution that I saw for the leaf decay mechanic on Bedrock was going to involve recursion for all the connected blocks to broken logs and leaves. This, I believe, is different than the way it is handled on Java but this is what I saw I had access to and understood.
For recursion to work and be performant, one thing you need to do is make sure to limit the depth of recursion. Because of Bedrock's leaf decay radius of 4 I had an easy answer for that. Another thing that usually needs to happen, especially in graph like structures (which blocks have through the 6 directional reference methods), is to make sure to only recurse over an entry once.
Javascript and equality
Javascript is two things, a great language and a terrible language. It has some weirdness that you would not expect, especially coming from other languages that don't have its history. For me this was with Block object equality and even the Vector objects representing the locations of those blocks. The Microsoft APIs were giving me new Block objects each time I referenced it from another method. The same thing happened with Vectors, which in my mind were closer to primitive and should have had proper equality already coded. Nope. When I tested if a block or location had been processed before, it always looked like a new object so it would be processed again. For an exponential algorithm like I was using this was a performance hit that I calculated shouldn't matter if it was limited to only processing blocks once.
The solution (for now, I know I can probably do better) that I figured out was to create a special class called a VectorSet that acts like a Set but you put the 3 dimensional vector objects in it and get all the benefits of a set. It worked right away, well after I made my initial code less confusing and understood what I was doing, and all of the sudden performance was acceptable again. From there I was able to get it working enough to start testing and stabilizing.
More performance improvements
Scripting in Minecraft is still limited and I had to trigger some commands. These aren't perfectly performant and asynchronous command calls are limited to 128 per tick. Because of this and because instantaneous leaf breaking is a bit disconcerting, I decided to break this up into processing chunks of leaves at a time. That improved performance even more and gave me another config option I could tweak to improve performance and feel.
Speaking of performance and feel, leaf decay on Java still feels really organic and I do like that. To get closer to that feel I added some randomness to the time between processing of leaf blocks to decay. More configurations to use.
Javascript ready, how do I Typescript
Once I had it working in javascript I wanted to clean it up by using Typescript. I was already using lots of comments to annotate the type information and it was ugly. Type information makes the job of coding much simpler so I wasn't going to remove those. I just needed to code in Typescript.
Honestly, the most difficult thing was dealing with myself and how complicated I thought it was going to be. The compiler that bridge. uses for Javascript will turn Typescript into the proper javascript when it exports it without any extra settings. Because of this, no config or manifest options need to be changed. Ugh, I should have started in Typescript.
Stopping the experiment
As I said before, I was using the experimental APIs because that is what I saw other add-ons using. However, this sucks for most players who don't want to use the experimental APIs. Every update can break them and that makes them less useful on public servers and realms. So, I created a branch to use the stable APIs and got to work.
I had been using several parts of the API that were experimental. So, when I turned them off, I had lots of errors and nothing was working. I wasn't sure if the stable APIs were capable of doing what I needed yet, but I was planning on seeing how far I could get and when the stable APIs caught up with my needs I would release it using the stable APIs then.
It turns out I was able to accomplish this task with the stable APIs. I probably started coding at the right time as some of my solutions used parts of the API that had only recently been made stable. I am glad I started with the experimental APIs though, for two reasons. One, the experimental based solutions were simpler for me to wrap my mind around so that when I went to the stable branch I only had to solve the problem of how to replace what I was doing before. Two, I think the solutions with the experimental API were a little more performant. So, as those become stabilized I will likely move back to using those (or at least test my hypothesis).
Testing on my realm
It worked on my local worlds, but what would performance be like on a realm. I have a realm that I play with people I love called Derp & Chill. It is descriptive of how we play when we are there and especially when we play together. It isn't a super busy realm but I wanted to make sure it worked and no-one noticed any (new) performance issues.
I heard not a peep. This can either mean no-one played and cut down trees or no one noticed performance issues. Either way, it was time to make it public.
Making it public
I was using GitHub but had it as a private repo. The work I did was fun and I hope it can help others make even more exciting things by adding it to there worlds and add-on packs. So, I made it public and shared with a very expansive add-on pack I use called Better on Bedrock.
When the creator saw it, I was reminded that this was new. No one had really done this on Bedrock so there was some excitement. He checked it out and said that he didn't understand what I was doing. Ah, right. I should comment my code and explain myself. So, now it is up on GitHub, with comments explaining what I was trying to do. It is ready for you to download from the release page and give your feedback.