Ok yes, I know I promised posts on Saturday and here I am on Sunday at 9:45 PM. To be fair, I was pretty busy this weekend, but don’t worry, I’ll be on time next weekend. I promise. Now, on to the title. You probably didn’t notice since it’s a very loose connection, but it is supposed to be a play on “Lights, camera, action!”. Now I’ll delve into each of those things in depth in their own paragraph, but just to summarize, I’ll be talking about the Lightning Storm zone, my solution to server-side proximity chat, and The Beast zone. Also this will probably be a long one not just because I have a lot to talk about, but my pride has been wounded that my friend has over double the words in his two posts as I do in my 10. So, on to the content!

Warning
When I said long, I meant long. I have a lot to talk about, and I'm talking a lot more in depth than I have before. If you get bored or if you make it to the end, before clicking away please fill out this form so I can get feedback on whether you like this format better, or the more brief type. https://forms.gle/QitVSw1mSdCAnDAYA


Lightning! Lots and lots of lightning!

So, the reason that working on this zone is big enough to include in the blog, is one thing: lightning rods. In case you aren’t aware, Minecraft 1.17 introduces lightning rods into the game, which can attract lightning around it, saving your builds or animals from lightning. So, for the lightning storm it starts out by a ton of lightning on the big tree, as you might remember from the Hunger Games, and then it will strike random blocks and have a chance to strike players at random intervals. For the random block and player strikes however, I want you to be able to divert it using lightning rods. Not yet sure how I’m going to balance it so that you can’t just camp in the zone as long as you have a lightning rod, but I’ll figure that out. So you might ask, what’s the issue? That’s already how lightning rods work! And, we arrive at my first issue. The way the Spigot API works is that when you call the strikeLightning function, it spawns lightning with a cause of CUSTOM. Of course, it should be easy to see that, and simply change the cause to WEATHER, right? Wrong! Here’s a little background knowledge. The Spigot API is a fork of the Bukkit API, which is simply a way to make it easier for developers to interface with NMS (net.minecraft.server). Basically, NMS is somewhat famous for being absolutely terrible to work with. Everything is obfuscated, and it’s just better to work with the API whenever possible. Unfortunately, NMS is what you have to deal with whenever you want something done that Spigot can’t do. It also means that if you want to see how something works, unveil the curtain and see behind the API, you have to delve into it. So, I’m sorry to report that it took me an embarassing long amount of time to figure out that I need to call the NMS version of strikeLightning, with a cause of WEATHER. Really kicking myself for that one. Anyway, moving on from that, we arrive at my second issue. I don’t think it would be particularly balanced to have a single lightning rod suck in all the lightning in a 128 block radius as in default Minecraft. And let me tell you, that one took way longer than the first one. Ok, so to detail my process, I began by thinking about how I could reduce the range, and noticing that it was a static final field of the lightning rod block, I figured that I could do my own lightning rod detection and call it good. Then remembered this thing called resources… it is neither fast nor efficient to loop through every block within the radius and look for a lightning rod. So then I figured, Minecraft is made by professional developers, it must have an easy way to get nearby lightning rods since it has to deal with that in the natural lightning strikes! Well… remember when I said earlier how it took me an embarassing amount of time to realize there’s a cause to lightning? Well this is that tenfold. When I said delving, I mean delving. It’s like crawling through spagetti style caving systems, but every time you think you’ve seen a landmark, seen the exit, you just realize that you’ve gone in a circle and have it a dozen times. But, yes! There is a way Minecraft does it, and it’s actuall quite interesting so I’ll try to describe it here. If you’ve played the game before, perhaps you’ve heard of entity data, or block data, or just NBT data as a whole. NBT stands for Named Binary Tag (yes, I had to look that up), and it can store tons of things about the entity or block. For a block like a chest, it might store the items inside the chest, or for an entity, it could store the health. There are dozens of different attributes, some of them specific to one block or entity, and many that are across all. Regradless, I doubt you’ve ever heard of chunk NBT data. I didn’t dive too deep into it because I value my sanity, but at least one thing it stores is Points of Interest. These can include all the different villager profession blocks, beehives, nether portals, lodestones, and….. lightning rods! Now, this is making it seem like it took a lot less time than it did to reach this conclusion, but trust me. This was a mess to find. Just to show you how terrible NMS is, this is my adapted version of the code I stole. Yes, this is after I cleaned it up a bit.

BlockPosition bp = new BlockPosition(loc.getX(), loc.getY(), loc.getZ());
WorldServer nms = ((CraftWorld) loc.getWorld()).getHandle();
Optional<BlockPosition> optional = nms.A().d((type) -> {
    return type == VillagePlaceType.x;
}, (blockPosition) -> {
    return blockPosition.getY() == nms.getLevel().a(net.minecraft.world.level.levelgen.HeightMap.Type.b, blockPosition.getX(), blockPosition.getZ()) - 1;
}, bp, radius, VillagePlace.Occupancy.c).map((blockPosition) -> {
    return blockPosition.up(1);
});

if (optional.isPresent()) bp = optional.get();
return loc.getWorld().strikeLightning(new Location(loc.getWorld(), bp.getX(), bp.getY(), bp.getZ()));

Anyways, it works now, so I’m happy. It was quite the journey, but I’m more knowledgable for it and I hope you are now too. 1/3 done!


Microphones and inputs, and WebRTC!

Alright, I may not have as much to say about this one as lightning storm, but then again, I didn’t think I’d end up writing 819 words for the last one (not counting the code block, in case you want to fact check me on that). So, proximity chat. You have perhaps heard of it before, but if not here’s the rundown. As your players walk closer in game, you sound louder to each other over the voice chat. As you walk farther, you sound quieter, until you go completely silent. This is to make it more like real life, where if you’re farther away you can’t hear as well. I want to put this in ReLife because it will add to the feel. You may have also heard of a proximity chat mod, but my goal is to avoid my users to have to download mods, if possible. So, my idea is that there is a web server hosted by the plugin. Using some java libraries, when you run the Minecraft server with the plugin, it would also be running the web server on your computer. I haven’t really thought about that part yet, and my demo currently has the web server hosted using NodeJS. How it works is the webserver hosts a socket server, and whenever someone makes a get request to that server, it returns a webpage. The socket server can also connect to socket clients that are run in JS on that website. So essentially, you connect to the web server, it gives you the website, then the website connects to the server again in order to send information back and forth. So my next course of action was to figure out how to access and transmit microphone audio across clients connected to the server. The idea then is that the server does all the figuring out who is close to who and what the different volumes should be. It then sends that information to the client, which adjusts the volume of the playback from the audio. I will have to figure out a way to not allow someone to tamper with the audio volume using the developer console, but I’ll think about that later. In my research about audio, I stumbled across the getUserMedia API. Essentially, this allows you to get the input of microphones and cameras from the user. It deals with asking permission from the user, and getting the input from the device to the JS code. I began by playing around with connecting the audio stream from getUserMedia to an <audio> HTMl tag, and voila! I could hear myself! My next issue was figuring out how to transport that across clients. At this point, I hadn’t had the idea of having the socket server at all yet, I just mentioned it earlier to paint a clearer picture. In my quest for transmitable audio, I came across this link several times, so I decided to read it: https://www.html5rocks.com/en/tutorials/webrtc/basics. And it is amazing! If you have any interest in this sort of thing, start with this article. It can explain things 100 times better than me. I’ll just mention here that there is an API called WebRTC that can handle all the real-time communication (RTC) for you. That was also where I saw the idea of hosting as socket server to have multiple clients connect to each other. To be honest, I pulled a lot of my current working code from this GitHub repository right here, so that is also an awesome resource if you’re looking at this kind of stuff: https://github.com/anoek/webrtc-group-chat-example. Anyways, putting that all together, I came up with a demo. I’ll explain it in words, but if you’d like to play around with it, the demo is right here, and the source code is here. It’s not pretty, but it’s just supposed to be a demo. As you open the webpage, you’ll be greeted with a popup asking for your name. After inputting it, you’ll notice that what you typed in appears in big letters in the center of the webpage. That doesn’t seem like much, but it’s actually sending information to the server socket, and then retrieving it. The idea for that is you will get a link in game, and that link will contain an automatically generated hash for your UUID. When the client sends that hash to the server, the server will then be able to associate an in game player with that client, and will send the Minecraft username back to the client to display on the webpage. Now, after clearing the input, you can see that there is a slider in the top left. If you drag it, nothing visible happens, but that’s because no audio is playing right now! That slider will adjust the volume once you connect another client. Now for this next part, if you’re following along, make sure your audio is on but not too loud. There will be auditory feedback if you don’t have headphones in. Pull up a new tab on the same URL, and and make a noise. Feedback! If you drag the slider… nothing again. So what’s up? The issue here is that you’re getting the same audio out of both tabs. If one is at 100% volume, you’ll hear that one since it’s louder than the one at, say, 50% volume. The feedback of course comes from the microphone picking up the speakers, and that goes on forever, so if you have the volume low enough or headphones in, that won’t occur, and you can just hear yourself. If you’d like to learn more about visual or auditory feedback, you should totally Google it, as it is an awesome topic I don’t have the knowledge or time to discuss. Anyway, if you move both sliders down, you should be able to hear a change in volume. That is simply setting the volume. Attribute of all the <audio> elements. And that about wraps it up! If you’d like to learn more about any of that stuff, as it’s super cool and I don’t understand it well enough to explain it properly, feel free to follow any of the links I provided or contact directly! Two thirds done!


Boom! Explosions!

I know, I know, explosions is the least like it’s counterpart in the original phrase (“action”), but it’s the best I could do. This will definitely be the shortest one, as The Beast was the easiest thing I worked on all week. And that says several things about what I worked on this week. Regardless, let me elaborate a little bit on what The Beast is. As I mentioned previously, it is a Wither-Ravager mutant that will hunt down players at record speeds, and burst through any protection you think you have. Now, time for the details! So, once again a rundown in case you aren’t a Minecraft player, though lets be real, you probably are. The wither is a boss in Minecraft, known for it’s three heads and withering flying skull projectiles. When you get it below half health, it goes into “melee mode” where it rushes in close to the player. Noting this, my colleagues and I kicked our gamemaster brains into action and we thought of another terrifying mob in Minecraft - the ravager. The ravager is extremely melee, bashing you with its head, and knocking you back quite a ways. Combining the speed and projectiles of the wither in melee mode, with the melee bites of the ravager, it is a fearsome duo. This is done by an NBT tag called Passengers. This tag can make one entity “ride” another one. The first issue I had with it was that it would regen above half health and exit melee mode. Simple fix, the Spigot API has an events system where all sorts of things from entities being damaged to worlds loading are processed. It allows you to change certain things about the event, or cancel it entirely. So, simply cancel the regen event if it would bring it above half health. Next issue, the bossbar was showing. Again simple fix - simply call the remove function on the wither’s bossbar. Now, on to the not so easy stuff. Something I forgot to mention previously… the wither passively targets non-undead insentient mobs - every animal and a few monsters. Unfortunately, that includes ravagers. So, when I’m not forcibly setting the wither and ravager’s target on the player, as in when there are no players in the zone, it is trying to target the ravager on it’s head. That means it flies straight up and shoots at the ravager. Stopping the wither from damaging the ravager is easy enough, another event, but getting it to not target isn’t so easy. What I ended up doing was having there be an invisible, invulnerable armor stand (the go-to entity for invisible markers) at the center of the zone that The Beast will target when there’s no players. Great, it doesn’t just fly straight up anymore! That sounded pretty easy, and to be honest it was pretty easy, just a little finicky to get the armor stand right. Now great, it is targeting you while you’re in the zone, and the center otherwise. The next issue is… what if you just hide behind a few blocks. Block yourself in and what can it do? Well, here’s another thing that I forgot to mention about the wither - when it’s damaged it will break blocks around it. My first instinct was to just constantly damage it for 0 damage to that it would destroy every block around it, but that means that it will flash red (as damaged entities to in Minecraft), and isn’t a great plan in general because of things such as damage invulnerability ticks - this thing’s just supposed to be very powerful, not indestructible. So, here’s the interesting part. There’s a field in the wither’s class, yes I’m going to just gloss over the digging through NMS for this one, that is how many ticks (in-game server ticks, everything runs off of them) until it will break blocks. So, set it to 1 tick and everything’s nice and dandy, right? Maybe you’ve begun to notice a pattern here but… no. Not at all. Unfortunately, that field is private. In case you aren’t a developer, private means that an outside object can’t access it - to get or more importantly, set it. And enter the last new API that I’ll be talking about in today’s post: Java Reflections. Once again somewhat infamous for not being general good practice. If something is private, it’s usually private for a reason. Regardless, there is a way to modify them which is extremely useful whem working with APIs that you can’t modify the source code of. Once again, I really recommend researching it if you’re interested, but it essentially allows you to get a field by name, then set that field to be “accessible”, then set the value for the object. Here’s an example:

Object obj; // the object with the private field
Object value; // the value to set the field to
Field f = obj.getClass().getDeclaredField("fieldName"); // get the field
f.setAccessible(true); // make the field accessible
f.set(obj, value); // set the value of the field for your object

Alright, enough with the examples, I’ll tell you how that’s useful. It means that I can now set this field, which was previously entirely inaccessible to me, to what I need. Now, it constantly breaks every block around it! Perfect. The final issue I was having was the wither getting distracted by passive animals and not attacking the player, because of how its pathfinding goals work. Once again employing reflections, I can simply use my own pathfinding goals! Finally, you’re done. Thanks for reading!

Alright, as I mentioned, I started writing this at 9:45, and it is now 11:25, but it was well worth it! I thoroughly enjoyed going more in depth with my writing, but I’m worried I may have bored some people who don’t know as much about programming. I’ve got a few ideas, like perhaps adding a TL;DR button for the longer posts, but please tell me what you think on this form! I’d love to hear some feedback on which style you’d prefer, and any other ideas you have for how I can improve the blog. Looking at this super long block of words, I may also consider changing the font. If you have any suggestions that would be awesome to hear as well. Thanks for reading all the way to the end, and see you next week.