Alright. So. Yes, it has in fact been, *gasp*, TWO WEEKS, since I’ve posted!! I know, I know, it’s truly terrible. But I’m here now! Anyway, as usual I’ll begin by explaining the title. To start with, Thanksgiving - I got a ton done over Thanksgiving break. I honestly don’t remember everything that I did off the top of my head, so as I’m writing this I’ll be scrolling through my Notion ToDo list page to remind me. By the way, if you’re ever working on a project and feeling like, this just isn’t organized enough and I’m going to forget all these great ideas I’m coming up with, just download a great note taking app like Notion and write it down. Trust me, it’ll make everything easier. Anyway, second part is 1.18! Yes, it finally released and it is amazing. If you haven’t checked it out, I fully recommend it, although I gotta say - it is way more laggy. Hopefully they’ll fix that in one of the minor updates. Anyway, I had to go and download the new Spigot version. I won’t get into how that turned into using Maven, but you’ll hear about that later - the final part of the title. Onwards!

Mappings (no, not the cartography kind)

I don’t know if you’ve noticed this, but I usually start with the simplest, and shortest, first. So, mappings! Now if you’re not a programmer, you’ll have no idea what these are, and even if you are a programmer you might not. Before explaining mappings, however, I must begin by explaining obfuscation. You may have heard the term, it means making to make something obscure or illegible. In terms of coding, it means converting your nice function names such as getPosition() to something such as a(). Generally obfuscation scripts (programs that run through other programs and obfuscate them) simply go in alphabetical order - a(), b()aA()eB(), etc. I’ve never seen a class go into triple letters, but I presume it would after getting through zZ. Anyway, it’s meant to make it hard for other people to read your code, making it harder for them to reverse engineer it. You’ve already heard me rant about NMS, so you know the feeling. That right there is essentially why NMS is so annoying, it’s hard to understand because everything is obfuscated.

So, finally back to mappings. When speaking of mappings in terms of obfuscated, or illegible, code, it means providing a key from the obfuscated function or field, with a name like a, to a name like position. I don’t know exactly how the files look, but that’s because there are scripts that remap, or deobfuscate code using mappings, NMS. I assume it’s kind of like a gobal find and replace for the different methods and fields. Now, that’s all fine and dandy, copy the remapped JAR files, rename everything, and voila! Nice code. Sadly… not so simple. The remapping renamed some of the classes and whatnot, but that was fairly simple to find and replace. The hard part is actually in the obfuscated methods and fields that I had figured out previously when I wasn’t using the mappings. I had a line of code such as:

nms.A().d((type) -> type == VillagePlaceType.x, (blockPosition) -> blockPosition.getY() == nms.getLevel().a(HeightMap.Type.b, blockPosition.getX(), blockPosition.getZ()) - 1, bp, radius, VillagePlace.Occupancy.c).map((blockPosition) -> blockPosition.up(1));

Yes, that is a lot to look at. And a whole lot of obfuscation. What the heck does .A().d( ... ) mean?? Well, after lots of struggling, I did fortunately figure it out, but it was not so easy as a find and replace. Anyway, next bit to talk about with this is Maven. You may have seen it in the categories, but Maven is a “build automation tool used primarily for Java projects,” and yes I got that straight off the Google page. Essentially, intelligent IDEs such as IntelliJ (see what I did there), or Eclipse, or whatever IDE you use can see your project is a Maven project, and then reading this file titled pom.xml, it can build your code with whatever configurations you need. It allows you to handle dependencies (other libraries you need to access in your code), as well as post- or pre- build actions (things to do before or after building your project). What I need it for is to use a script created by one of the Spigot developers to re-obfuscate my code. The JAR file that runs a Spigot server on my computer uses the obfuscated code, however my code is referencing the remapped classes and functions, so at runtime (when I load the plugin into the server), it will error. This is solved by adding a post-build action that re-obfuscates the JAR file. If you want to learn more, I encourage you to look it up, but it is a very helpful tool. IntelliJ makes it really easy to migrate thankfully.

Anyway, the whole reason I saw that the mappings exist is that when I was looking to upgrade to 1.18, I hit a roadbump. It’s not so easy as downloading the new JARs and chucking them into the folder, no no no. Recall that prior to this, I was using obfuscated code. And the thing about obfuscated code is that every, single, update, it changes. Every little bit of obfuscation I figured out what it did and included it? Now needs to be redone. So, I was looking up a better way of doing it and I came across a thread made by one of the developers of Spigot talking about when 1.17 released, and saw the Mojang Mappings. It required a bit of fiddling around, but after following the Spigot instructions on how to setup Maven, and then copy-pasting some code into the config, it worked out. Now about 1.18…

1.18 Backend Update

Like that seamless transition? I thought it was pretty good. Anyway, you’d think that 1.18 wouldn’t actually affect too much, since it mainly changes biome stuff and terrain stuff, but lo and behold, something broke! It wasn’t too bad to fix, so this will probably be pretty short, but here goes. I was going through the different zones and testing them, just to make sure none of them broke even though they shouldn’t have, and something extremely strange happend. The poisonous fog zone worked, but the tsunami zone didn’t! Now I know I haven’t mentioned the tsunami zone before (whoa big reveal!!), but I’ll just explain it in brief here and more later.

Essentially, similar to the poisonous fog zone, there would be an inner bounding box that expands every cycle so that the affected area expands over the course of the zone to eventually fill up the entire bounding box. The tsunami has a wave of water that moves forward every cycle, and then after it passes, the water left behind starts to lower down (setting the height level of the water to make it look smoother). Anyway, they use the exact same method of an inner box and outer box, but the one difference is that the tsunami’s inner box is only the wave - one block thick. The poisonous fog’s inner box doesn’t move forward, it only expands forward (with the tsunami, it expands the back forwards as well as the front so it “moves” forward). Now what I presume changed is that for some reason, in 1.17 when you expanded a bounding box, it wouldn’t expand to be smaller than one block in any direction, while in 1.18 it can have a width of 0. This means that when I innitialy make the inner box (which is supposed to just start as one block wide at the very back of the outer box), instead of having a width of 1, it has a width of 0. This means that for the poisonous fog, it can simply keep expanding and the width will get bigger as it should. For the tsunami however, when it expands both the front and back, the width remains 0, meaning it never sets the water. That’s just speculation, but it’s the only explanation I can think of. Pretty simple fix all in all, but I’m sure you’re just dying to hear about it more in depth, as well as what the heck I mean by Thanksgiving.

Turkey Slaughter Day

Sorry, sorry, I know the heading is a bit morbid, but in my defense it’s true. Moving swiftly on from that, I’m on a roll with transitions! So, Thanksgiving. More specifically, Thanksgiving break and what exactly I spent my break doing. As well as an explanation on why I didn’t post last weekend. So essentially, I spent my break working on ReLife (as you might have guessed), and more specifically, I decided to wrangle the gargantuan task of… kill credit (dun, dun, duuuuun)! It was certaintly a behemoth of a goal, but I am happy to say I achieved it. And that is the main reason I didn’t post last week, I felt somewhat intimidated by the prospect of writing it up, and school was starting again so I didn’t really feel like it. I’m here now though, and boy oh boy do I have a lot to talk about!

Since I just mentioned it, I’ll start with kill credit, but I’ve also got a few other things to talk about. Some of you are probably wagging your finger at your screens. How silly of you, Minecraft already tells you when you’re killed by another player, you can just use that! Oh how wrong you are… how wrong indeed. To explain, from the beginning of ReLife, I’ve wanted this to be a mini-game that doesn’t require PVP (player versus player) skills. PVP can include a lot of things, literally any kind of fighting, but in Minecraft it generally refers to combat specficially (swords, bows, etc). Sure they’d be helpful, but not required. I’ve always had in the back of my mind that traps, and the way you kill someone, should be extremely imoprtant. So if you recall, I believe I’ve talked about Renown before, but if not (or if you forgot), you essentially start with an amount of Renown (TBD, 10 currently), and whenever you kill someone, you steal a percentage of their Renown, based on how you killed them. Renown can then be used to buy things such as sponsorships (items), but I’ll get more into spending them at a later date, right now I’m talking about earning them.

Basically, every time you damage someone, and I’ll get into the expansive ways of damaging people in a little, you are added to their “credit list”, which is a running list of who has credit for your death. After a certain amount of time (currently 7 seconds), you are then removed. That list also stores how you damaged them (what type), and how much you damaged them for so if you just do half a heart, it won’t be weighted as much as four hearts. When you die, it then goes through your credit list and gets your “spectacle” and “contribution”. Contribution is a number 0 to 1 of what percent of your health that player dealt, as well as number 1 to 10 that is specific for the different types of damage. For instance, suffocation gives a lot less contribution than an explosion. For projectiles, it overrides that to take into account distance. Spectacle is another number 0 to 1 defined by the type of damage, and is how “cool” the damage is. For instance, explosions are, once again, a lot “cooler” than suffocation. This is how I balance traps versus combat - because lets be real. Traps are waaaay cooler than wacking someone with a sword. Not to mention the fact that this is true to the insipration - the Hunger Games themselves. In the books, the citizens of the Capital watch the Hunger Games for entertainment, and if you are more entertaining, you get more sponsors and whatnot.

Getting back to when you die, the percent of your renown that you lose is the highest contribution of all the types of damage in your credit list. The total renown that is then split up among the players in your credit list is your renown multiplied by the highest spectacle of all the damage. The total renown is also how much you lose - lose in a more spectacular way and you’ll lose more renown. It then takes that total renown and divides it up among all the players, where the amount of renown you gain is your contribution divided by the total contribution (what percent you contributed) multiplied by the total renown. If that was confusing, your contribution what percent of the total renown you gain. Now, here are all the ways you can gain credit:

Contact - such as standing on a cactus
Entity Attack
Entity Sweep Attack - when you’re hit by the sweep attack of a sword
Projectile
Suffocation
Fall
Fire
Fire Tick - when you’re taking damage from fire, while not being in a fire-inducing block
Lava
Drowning
Block Explosion - explosion originating from a block, such as a bed in the nether
Entity Explosion - explosion originating from an entity, such as a creeper, or primed TNT (yes, TNT turns into an entity when primed)
Lightning
Poison
Magic - as far as I can tell, only splash potion of harming. Lingering harming produces an entity attack from the area effect cloud, and normal potion is an attack from yourself
Wither
Falling Block - a falling block such as an anvil landing on you
Thorns - damage taken from attacking an entity with armor with the thorns enchantment
Hot Floor - seemingly only from magma block (damages you when you walk on it)
Freeze - freezing in powdered snow
Cramming - when you place too many entities in the same block and any more entities begin to take “cramming” damage Piston - pushing someone or pulling the blocks from beneath someone with a piston
Indirect - fishing rod, spleefing (breaking the block beneath) someone, or spleefing someone using an explosion

Alright, that was a bit of a long list, but there you have it. Every different type of credit. They aren’t all necessarily damage, but most of them are. I’m only going to go in depth into a few of them (the more complicated ones), but here’s a general explanation that should cover most of them. Every time you place a block, I add a metadata tag (persistant data stored by block that doesn’t get reset when the server reloads) that stores the “owner” of that block. I had to cover special cases such as fluids flowing, cactus growing, fire spreading automatically, blocks with gravity (such as sand or anvils) converting from blocks, to falling block entities, back to blocks when they hit the ground, projectiles being fired, and entities being placed such as boats on the ground or minecarts on rails. Those are pretty much all the exceptions to that general rule, however there are more in depth exceptions I’ll explain in a second. Essentially whenever you receive one of those simpler types of damage, such as lava damage, it will check the block that damaged you, in this case the lava you’re standing in, and then add that person to your credit list, in this case with the LAVA type, and however much damage was dealt. Onto the exceptions!

Most of the exceptions revolve around one thing: redstone. In case you aren’t aware, redstone is kind of like electricity. Kind of. Very loose analogy but here goes. Essentially, there are source blocks such as block of redstone, redstone torch, lever, button, etc. You can then use things such as redstone dust, repeaters, and comparators to transport that redstone into devices such as pistons, dispensers, droppers, etc. Those are the basics, but because of my focus on traps I need to make it so that ownership travels through redstone. Essentially, every time redstone fires I need it to set the owner of all the redstone around it to its owner so that if you build a trap, but then someone else comes along and uses it for their own gain, it’ll give them the credit.

I accomplished this by making it so whenever you interact with a an interactable redstone power producer, such as a lever or button, it sets you as the owner of tha block. Then, that block triggers a redstone event, because that’s how Spigot works. That redstone events sets all the redstone around it (whatever redstone wires, repeaters, or whatever) to the owner of the redstone. That then travels all the way to the destination, where it sets the owner of the dropper, or piston, or whatever to you. For dispensers I just had to add a simple check so whenever it dispenses something that turns into a block, such as water or lava, it sets the owner of that block, or if it dispenses something that turns into an entity, like a boat or a minecart or a potion, it sets the owner of that entity. Droppers are essentially the same as dispensers, but don’t shoot items as far, and drops everything as an item rather than some as a block or entity. For both droppers and dispensers, when they drop and item, it needs to set the owner of that item. The reason why, is that say you dispense an item into a water stream, which then carries the item down to a hopper (a block that picks up items), it needs to set the owner of that hopper to the owner of the dispenser that shot out that item. So, another event on hoppers picking up items, and on hoppers transfering items as that isn’t a redstone event, and voila! It works exactly as planned.

Ok well that is the shrunken down version of redstone, because honestly that probably took 2+ days of break to finish. Next is pistons. Essentially, if a piston pulls a block out from under you or pushes a block into you, or just plain pushes you, it needs to give credit. This is because if it drops you into a pit or pushes you off a ledge, you need to get credit for it. Thankfully, Spigot has events for piston extend and retract! Basically on those events, I check for people above as well as people in front, and then add the owner of the piston to their credit list. Unfortunately, another pistons have another property, while wonderful in game, horribly annoying to deal with in code. When you use a piston to push certain blocks into certain other blocks, it triggers a chain of block updates, and should you observe one of those blocks far away using an observer, a block that looks for block updates, it won’t set the owner of said observer.

Now sadly, this is not such an easy issue to deal with. There is a block update event, but according to the documentation, that event can be called thousands of times per second on an active server. So now I have to deal with making sure I don’t lag the server out. Which of course I ended up doing several times during testing. Basically I have to check every physics event if the state is being updated, and if so, then set the owner of the updated block to the updater. It required quite a bit more testing and remaking than I described here, but that’s the boring part.

Another issue was dripstone - if you push or pull or break the block above a stalactite, it turns into a falling block and damages anyone it falls on. Fairly simple, just added an exception for dripstone so it sets the owner of all the dripstone that would fall. You also take extra falling damage from dripstone, but that is handled by CONTACT damage. The final thing, similar to falling on stalagmites, is other types of fall damage, but rather than contact from falling on stalagmites, it’s FALL damage. The main things that I can think of for fall damage is spleefing someone, blocking off water (which negates fall damage), placing sponge to absorb water, or using frostwalker (an enchantment on boots that transforms all the water around you into ice). That was honestly a pretty quick fix, I just have a list that stores whenever you place a block for the same amount of time as the credit list, and then if you fall on an owned block, it gives that person credit. For frostwalker and sponge, there are thankfully events that handle those.

Ok, I’m not sure if those last couple of paragraphs were any interesting, because I was writing them at like 11 at night. Kill credit was super fun to make though, and I had some friends black box test it. In case you weren’t aware, black box testing is where you have a consumer test it, or someone who doesn’t know how it all works behind the scenes. The oposite of that is white box testing, where a developer, or someone who knows how it all works, tests it. They’re both useful for different things, but in this case I wanted to test how robust my system was against an actual player designing traps. In the end, from both white box and black box testing, I of course found tons of bugs and got them all sorted out.

The last thing that I worked on over Thanksgiving, before I get into last week, was bodies. Essentially I wanted to make it so that when you die, it creates a body on the ground that’s a copy of your skin except laying down, that other people can loot or revive. Alright, that’s a fairly big task so let’s start at the beginning. What I need to do is whenever you die, it creates a body and puts you into spectator form. However, I don’t want you to see the respawn screen and then have to click respawn and everything, so instead I just check on a damage event and if the damage dealt would excede your health, create a body. Now unfortunately there is not a method in the Spigot API for creating dead bodies, so I had to do some things that are… a little bit strange. Ok, a lot strange.

First things first I need to explain how a dead body actually works. If you’ve ever played a minigame with dead bodies you might know kind of what I’m thinking of. Essentially, it looks like you and is flat on the ground. I already knew from previous experience how I was generally going to go about this. Of course, in NMS there are classes to deal with players. Makes sense, as players are an entity and have to be handled by the server. The interesting part happens when you create players.

Yeah you read that right, what happens if you instantiate the ServerPlayer class and add that entity to the world? Well… it would error. That’s because players have something called a PlayerConnection. That connection is what handles sending packets back and forth to the player, which is how Minecraft servers communicate with the client. Side note, hacked clients work by spoofing packets and saying you’re doing things you’re not actually doing. Anyway, were you to simply instantiate a new player, it wouldn’t have a valid connection and therefore would crash the server. I know from experience. You could always set that connection to be the same as the player who died but… that is a terrible, terrible idea that breaks a whole lot of things. Again, experience. So, if you can’t add the entity server side, you’re going to have to add it client side. What this means is that the server doesn’t actually know it exists. When you send the add entity packet to the client, it will add the entity there and the entity will show up and be visible, but you won’t actually be able to interact with it. Now, that’s a problem that I’ll get to later. For now, yay! There’s a Steve skin or Alex skin (the default minecraft skins) standing in front of you. How exciting!

Now, the next part is setting the skin. I already knew this from 13Q experience, but I first learned about it just by Googling. Google is a developers best friend, you don’t need to reinvent the wheel. How it works is through another field players contain: a GameProfile. That profile stores the UUID of the player, the name of the player, and most importantly, the PropertyMap of the player. As you might guess, a that property map stores a map of the properties, and each property consists of a name, a value, and a signature. I’m not sure exactly what the signature does, but it’s used in skins. All you have to do from there is set the “textures” property of the fake player to the “textures” property of the real player and there you go! The skin now shows up.

However, you may notice something about the skin. Essentially, how minecraft skins work is that you can have an outer layer to it, which you can disable in your settings. However, if a skin has those layers, it will be disabled by default on the fake player, so it might look a bit strange. Mine certaintly does. This is a not as simple, but still pretty simple fix. Every entity has a SynchedEntityData, which is a class that stores a whole bunch of information about the entity. It’s “synched” data, because that data is synched between the server and client using packets. When I first noticed the issue, I looked up how to fix it and was directed to a byte with id 17 - the displayed skin parts. While sounding somewhat disgusting, that is exactly what I need. If you add up all the bytes that represent different skin parts together, you get the entire skin - 0x7F or 127. Once you set that value… nothing changes. That’s because there’s a second packet that gets called when you set the entity data, and it doesn’t automatically check that when you send the add entity packet. So, after sending the entity set metadata packet, it works! Next step.

You now need to make the body flat on the floor, as a dead body isn’t exactly going to be standing straight up. If you are familiar with Minecraft, one entity position might come to mind: sleeping. When you’re sleeping in a bed, your player entity lies flat on the bed. Luckily for me, there is a nice function, well it wasn’t so nice before remapping, called startSleeping, that takes in a position. Well, I gave it the position and yes! The body is lying down. But… what’s that? It’s hovering in the air? Well it actually makes sense and I saw it when I was looking at what that function actually does. When you’re sleeping in a bed, you aren’t directly on the ground because the bed is raised a bit. For some reason however, when I set the position to offset the bed, it still doesn’t work! I’m not entirely sure why, but setting the player to sleeping obviously isn’t going to work. Instead, there’s a nice function called setPose. Using this, I can directly set the pose to sleeping without worrying about all the repercussions of actually starting sleeping. A quick offset later so the body isn’t just in the ground, and it works! Finally, a body with the right skin, lying down, spawned at the feet of the dead person.

Final step before dealing with the really weird part is setting the player to spectator. Man, this is an interesting story. It starts all the way back when I was working on Artifact for 13Q (which I still want to get back to at somepoint after ReLife). When you’re a spectator in Minecraft, you get a few liberties. You gain spectator tools that allow you to teleport to players, as well as the ability to fly through blocks. Now for Artifact, I wanted neither of those. I wanted you to have your hotbar hidden, but not get the spectator tools, and I wanted you to fly, but not get to fly through blocks, and I also wanted the name in tab to not appear italic as it does when you’re a spectator. Now this is accomplished in a strange way. I was tinkering around with packets and whatnot doing some weird things, and this is what ended up happening. There is are a couple of functions in the Spigot API called showPlayer and hidePlayer, which hides or shows one player from another both in game and in the tablist. Sadly, that doesn’t work on yourself. However, what you can do is send a packet. What happens is that when you send a player info packet with the action of removing yourself, you can no longer see yourself in third person, or in the tablist. But then, if you set their gamemode to spectator after that, interesting things happen because of server-client desynchrony… exactly what I want to happen. It sets the player into a perfect hybrid of survival and spectator modes. So, I do that, set the player’s flying speed to 0 so that they can’t fly away, and I’m now mostly done. The last thing I have to do is just cancel spectator events so that they can’t spectate random players walking by and escape (in Minecraft if you right or left click on someone as a spectator, you start to spectate them).

Ok, that was a bit of a long paragraph. Last thing on bodies is being able to loot and revive them. I ended up making it so that when you sneak-right click on someone, it pulls up the revive menu, and when you sneak-left click on someone, it pulls up the loot menu. But not so fast, remember when I said the body’s only client side so you can’t interact with it? That means that I now have to have some other entity invisible on top of the body, an entity that is the same size, and then check if a player interacts with that. Well you may not believe this, but two dolphins on either side of the body just about fits the bill! There sadly isn’t any Minecraft mob that does exactly what I want, but two dolphins covers it pretty well. All I have to do is create invisible, invulnerable, silent dolphins, and then check those for interaction. That required a lot of noodling around with to get to work, but work it did.

That just about wraps up bodies! Once again, that required a lot of messing around with, but I’m happy with where they are now. Alright, finally onto the second to last thing. This one is probably both the most and least complicated thing I’ve talked about yet. I was thinking about the issue of players being able to just stack up and get above the zones. It isn’t really reasonable to extend the zones all the way to the ceiling, so instead I have a nasty little surprise if you try to skybase. It’s a new zone, I’m calling it TNT attack, and it’ll extend all the way across the top of the arena. I was brainstorming this with my primary consultant and mathmetitician, and he helped me come up with the idea of lobbing TNT at you from across the arena. To elaborate, it will figure out what spot is directly opposite you on the other side of the arena, then send TNT in an arcing (KEY WORD!!) motion at you, which will then explode upon reaching you.

I really can’t talk too much about the math behind this, because to be honest I don’t understand it. Apparently getting the other side of the box was fairly easy, but then comes lobbing the TNT. That arcing motion is important because if it were to just shoot at you in a straight line like a laser, it would do two things: A) be a whole lot easier to code and B) look way less cool. I just like the idea of flying TNT coming at you. Anyway, I was told it involved a variety of math from eigenvectors to kinematics. No, I don’t know what eigenvectors are either, but apparently they’re some linear algebra thing. Who knew? Super advanced math is actually useful. Anyway, I really don’t have much talk about here because the math was the big part. Honestly he wrote it more than I did.

Alright, tsunami zone. This was one of the first things I wrote about and will be the last for today. As I already mentioned, it’s similar to the poisonous fog zone, with an inner and outer box, but instead of a constantly expanding box, it’s a constantly moving forwards box. I’m going to just give the basic run down of this, because today is Sunday and I have a ton of homework I need to get done still. Essentially, every cycle it moves forward and sets all the blocks in the block to either water if it’s air, or or if it is a water loggable block, it sets it to be water logged. It then loops through all the previously set blocks and lowers their water level according to a math function my associate from earlier created for me, which puts it in a sort of exponential curve where the tidal wave is way higher. It then checks for any players in the box and damages them and shoves them forward, as a tidal wave would do. The idea is that the wave will move over everything and cover it in water, and then over the course of the zone it will lower the water until it’s all drained.

Wrapup

Ok sorry for not getting too in depth on that last one, but I really do need to get to homework. Once again, if you have any suggestions or want more information on any of this feel free to contact me and I’d love to hear what you have to say. See you all next week, and I hope you had a wonderful Thanksgiving!