Note: If you're not interested in the inner workings of Minecraft and modding, then this long-winded post is probably not for you!
We all enjoy modding today thanks to the work of the MCP and Forge teams, but there was obviously a time when there was no MCP and Forge. People had to manually disassemble/decompile the various classes and try to figure out what did what so that they could write even the simplest JAR mods. I believe there may still be a handful of people who try to do this now, like Immibis, who makes mods like Podzol to Diamonds not long after a Minecraft version gets released. But overall I think that kind of modding is mostly a thing of the past.
Well, I decided I wanted to see if I could do this myself. But not just write a simple mod; I wanted to be able to do anything to the game, regardless of whether it was releasable. And I wanted to do it to a specific version. In other words, I wanted to decompile and deobfuscate the game, by myself.
The subject/victim of my project was alpha 1.1.2. It was the last version before biomes were introduced, had a noticeably different look and feel, and obviously the simplest of all the versions I like to play with. It also had specific characteristics that I wanted to try to mod right away, such as making food stack, adding your coordinates into the F3 info, making the F3 info toggle on and off instead of only showing when you hold the key, burning wood tools, creating charcoal from logs, shift-click items from the crafting table, etc. Mostly modern conveniences, for starters.
Well since Mojang obfuscates their code, it makes it tricky to decompile. It not only randomizes all of the methods, variables, and class names, but it adds in junk bytecode and such into the compiled class files to confuse any automated programs trying to decompile them back into Java. I figured I would be able to deal with this, because I've decompiled DOS games before (such as Legend of the Red Dragon) and managed to thoroughly label their guts, and the mess of assembly code you initially get from doing that didn't seem like it would be much different than this. Reconstructing Java is a hundred times easier than reconstructing C or Pascal, because no matter how much you try to obfuscate it you're still going to leave behind certain bits of information due to the nature of the language. Plus, I already have Minecraft modding experience, so I had an idea of what goes on inside the game already.
The mistake of course is to ever assume something will be easy.
Minecraft is huge. Even ancient alpha 1.1.2 is a significant amount of code. When I decompiled it (using JD-GUI), I was faced with hundreds upon hundreds of Java errors to fix. This was expected, and many of these weren't hard, sometimes fixing half a dozen in one swoop with some of the automation of Eclipse (a great Java editor). But there were some that I just didn't understand. It was trying to pass arguments to functions which didn't take those arguments, and just a myriad of other conflicting and confusing things in the code.
The biggest problem is that obfuscation causes tons and tons of name clashes in the code, such as multiple functions simply named "a", even inside the same class, where only the arguments passed to that function determine the proper function to be called. This can be a nightmare to deal with.
I went through this code by hand and tried to fix it to the best of my ability. But at some point, as I learned the process better, I realized I'd probably made mistakes earlier on. I'd come up with a better method of resolving conflicts, by looking directly in the disassembled bytecode listing instead of digging through the decompiled Java, and using that as the reference to find out the proper argument types to pass to a function. Slower, yet far more reliable. But it was too late to go back, there was no way to know where the mistakes would be. All I could do was keep going forward, and hope I could fix it once I had something that ran.
This entire process took hours, mind you.
The moment the error count dropped to 0 was a happy time. I had questioned whether I'd even get that far after all the issues I ran into. So, I ran it, and it didn't crash!
Broken. And not only visually, but it wouldn't respond to mouse clicks, either. I wasn't entirely surprised, so I set out to try to fix it. I dug around and dug around, for quite some time, until I eventually said screw it. I'd messed this up most likely, and I was never going to find my mistakes.
So I started over.
It went faster this time. I was able to copy some of my work from the previous attempt, files which I knew I'd done properly. It still took quite some time, but I eventually knocked all the errors out yet again, held my breath, and launched it.
Exact same thing.
A bit more disheartened now, I kept trying. I decided to focus on the mouse input problem specifically, since I wasn't any closer to figuring out the graphical one. And keep in mind, this code is still completely obfuscated, you have no idea what does what, you only managed to get it to compile again. So I traced and traced, sending debug output here and there, finally working my way back to a function again which I'd looked at to start with and dismissed. It wasn't being called from anywhere, and I'd initially assumed it was unused code, but that made less sense after I found my way back to it again.
Turns out, it was the crucial code. The decompiler had interpreted the bytecode incorrectly, and was never calling it at all! I rewrote this tiny portion to actually execute it, and suddenly the title screen worked. The graphics weren't broken at all, that's just the way it looks the moment before the blocks come flying down to spell the logo! The input worked now as well. This function was obviously crucial to handling timing and input, and it simply wasn't being called. What a relief. I realized that I might could have even avoided decompiling the second time if I'd found this sooner!
So then I did the ultimate test, and started a game. That's when everything truly went to hell. This screenshot came only after I'd stopped frequent crashing by commenting out things like entity updates.
This one was the disaster. It was hard to tell where to go because nothing was labeled. I disable various things just to see if it even made a visual impact on the game to know if I needed to go deeper there or not. Eventually I came to the conclusion that there probably was just no fixing this. Throughout the process so far I'd come to realize that the issue with so many of the obfuscated names being the same, only differentiated by their arguments, was the death of it. They could be littered throughout the code, calling the completely wrong functions, yet not throw any compile errors because technically the arguments are correct.
An example of this might be one function that wants three integers, and another function that wants three floating-point values. In Java you can pass an integer to a floating point function and it won't complain. But the problem is that if both functions have the same name, then the wrong one is going to get called if you pass those integers but were wanting the floating-point one. Java doesn't have this problem normally because the compiler tracks this stuff. But when you're going in reverse it's up to the decompiler to figure this out, which is much trickier. And mine wasn't doing it.
I dug through and fixed some conflicts in the rendering code which I was able to find, but it made no difference in the end. There was too much code, I could never go through every single class and match up every single argument type of every function call to make sure they were correct. That would take me days, or weeks. I figured that was probably the end of that little project!
But since I'm stubborn, I later decided to look for a different decompiler instead. I tried a few, some seemed to have better results than others. But then I decided I'd give Fernflower a try. It's the one that MCP uses, so I figured if it works for them, maybe it can work for me, even if I wouldn't have the patches and other tools they use when you set MCP up. I honestly didn't know what to expect.
Fernflower has a lot of options, and it took finding the right combination until I got code produced that I felt I could work with. Fernflower was doing some great things, like actually recognizing the arguments a function needed and trying its best to match things up. I was pretty optimistic, especially given that there were far less errors to fix in the code than in any of my previous attempts. I still had to do some manual bytecode comparisons for the occasional issue, but the significant amount of it was easy but tedious fixes. I was able to use some of my old code again for things I knew wouldn't cause issue, which also helped.
So then that magical moment came again, when there were no errors. And I ran it. Everything worked on the title screen, first try. Awesome. My faith in Fernflower skyrocketed. So I did the ultimate test of trying to create a new world. And that's when it just froze, with my computer practically seizing up as well. When I eventually got the task closed, I tried again, but only to the same incredibly strange result. It was absolutely killing my computer, pegging both cores (I only have a wimpy dual-core). This was far from the result I expected.
Well here we go again, I thought. This time though I suspected a code loop, so I put it through the debugger, waited until the game got stuck, and paused execution (after my computer caught up with my mouse clicks again). It didn't take long to trace my way to what was indeed a code loop. Fernflower had misinterpreted some bytecode slightly differently than it was intended. I fixed that, and then the amazing thing happened.
It ran. It created a new world. I believe there was possibly one more crash after this, related to chunk sorting or something, which I was able to resolve (with code that didn't match the bytecode, oddly enough, but that made sense in terms of Java). I just sat there and played it for a few minutes, because I couldn't believe I'd finally made the damn thing work.
I didn't have any sound, though. This was a known problem with alpha 1.1.2. So I went and grabbed the a1.1.2_01 jar from my MultiMC, did a file compare on all the class files, and pulled out only the ones that were different. That was only about three or four files maybe. I disassembled and decompiled these, added those very few bits of changed code into the game (including the version labels), and ran it again. Sound worked. Everything worked. Cool beans.
Even though absolutely nothing was labeled in the code yet, I'd found where the version string is printed on the screen when doing the sound patch, so I put my own little tag into the game as my first modification.
After saving my work thus far in case of terrible disaster, I immediately set forth deobfuscating the code. It's like a puzzle; you know what the final thing is supposed to look like, you just have to figure out what each piece is. And there's a lottt of pieces. But, again, like a puzzle, when you figure out some pieces, other pieces fall into place. It can be pretty addicting, and I'd forgotten that feeling from back when I decompiled games in the past. You want to find the next one, and the next one. Just figure one more class, then you'll quit for the time being. At least, until that one kind of makes another one make sense, then you need to figure that one out before you lose your place.
Some of this time is also spent changing things while you're in-game to figure out what they do (which Java can do, code hot-swapping, it's great), so you're also having fun playing the game while poking its guts.
In the last couple days I've deobfuscated a decent amount of the code, but still not even half of it. I at least covered the major things, including most of the blocks and items. It's enough to start doing some basic modifications though.
If you're not familiar with that version then you might not notice right away, but that is indeed 9 pieces of pork stacked! I limited the stack to 16 to compromise a bit, and it'll affect both raw and cooked pork since they share the same base class. Now that I think about it, it should affect everything else that uses that class too, like bread and apples, which is also fine! It might seem like an insignificant change to the game, but to me this was the goal, which I somewhat surprised myself by achieving after all of the failure along the way.
Anyhoo, I realize that this is a really long post, and might not even be particularly interesting to a lot of people. But I thought I'd share this with everyone, in case it helps them have a bit of insight into this process as well. Or maybe it just makes you want to play an old version of Minecraft again, which is fine too!
Just keep in mind that this isn't all the MCP team does. All I did was basically decompile and deobfuscate the game (or part of it, so far). They not only deobfuscate it, but also create all of the mappings and the process to re-obfuscate again, which is a crucial part to being able to release mods! I would like to attempt this as well, but it was only briefly before starting to write this post that I realized that the first version of MCP ever released was, ironically enough, for Minecraft v1.1.2_01. Had I realized, I might have simply downloaded that, even, and saved you from this terribly long post.
Note: If you're not interested in the inner workings of Minecraft and modding, then this long-winded post is probably not for you!
We all enjoy modding today thanks to the work of the MCP and Forge teams, but there was obviously a time when there was no MCP and Forge. People had to manually disassemble/decompile the various classes and try to figure out what did what so that they could write even the simplest JAR mods. I believe there may still be a handful of people who try to do this now, like Immibis, who makes mods like Podzol to Diamonds not long after a Minecraft version gets released. But overall I think that kind of modding is mostly a thing of the past.
Well, I decided I wanted to see if I could do this myself. But not just write a simple mod; I wanted to be able to do anything to the game, regardless of whether it was releasable. And I wanted to do it to a specific version. In other words, I wanted to decompile and deobfuscate the game, by myself.
The subject/victim of my project was alpha 1.1.2. It was the last version before biomes were introduced, had a noticeably different look and feel, and obviously the simplest of all the versions I like to play with. It also had specific characteristics that I wanted to try to mod right away, such as making food stack, adding your coordinates into the F3 info, making the F3 info toggle on and off instead of only showing when you hold the key, burning wood tools, creating charcoal from logs, shift-click items from the crafting table, etc. Mostly modern conveniences, for starters.
Well since Mojang obfuscates their code, it makes it tricky to decompile. It not only randomizes all of the methods, variables, and class names, but it adds in junk bytecode and such into the compiled class files to confuse any automated programs trying to decompile them back into Java. I figured I would be able to deal with this, because I've decompiled DOS games before (such as Legend of the Red Dragon) and managed to thoroughly label their guts, and the mess of assembly code you initially get from doing that didn't seem like it would be much different than this. Reconstructing Java is a hundred times easier than reconstructing C or Pascal, because no matter how much you try to obfuscate it you're still going to leave behind certain bits of information due to the nature of the language. Plus, I already have Minecraft modding experience, so I had an idea of what goes on inside the game already.
The mistake of course is to ever assume something will be easy.
Minecraft is huge. Even ancient alpha 1.1.2 is a significant amount of code. When I decompiled it (using JD-GUI), I was faced with hundreds upon hundreds of Java errors to fix. This was expected, and many of these weren't hard, sometimes fixing half a dozen in one swoop with some of the automation of Eclipse (a great Java editor). But there were some that I just didn't understand. It was trying to pass arguments to functions which didn't take those arguments, and just a myriad of other conflicting and confusing things in the code.
The biggest problem is that obfuscation causes tons and tons of name clashes in the code, such as multiple functions simply named "a", even inside the same class, where only the arguments passed to that function determine the proper function to be called. This can be a nightmare to deal with.
I went through this code by hand and tried to fix it to the best of my ability. But at some point, as I learned the process better, I realized I'd probably made mistakes earlier on. I'd come up with a better method of resolving conflicts, by looking directly in the disassembled bytecode listing instead of digging through the decompiled Java, and using that as the reference to find out the proper argument types to pass to a function. Slower, yet far more reliable. But it was too late to go back, there was no way to know where the mistakes would be. All I could do was keep going forward, and hope I could fix it once I had something that ran.
This entire process took hours, mind you.
The moment the error count dropped to 0 was a happy time. I had questioned whether I'd even get that far after all the issues I ran into. So, I ran it, and it didn't crash!
Broken. And not only visually, but it wouldn't respond to mouse clicks, either. I wasn't entirely surprised, so I set out to try to fix it. I dug around and dug around, for quite some time, until I eventually said it. I'd messed this up most likely, and I was never going to find my mistakes.
So I started over.
It went faster this time. I was able to copy some of my work from the previous attempt, files which I knew I'd done properly. It still took quite some time, but I eventually knocked all the errors out yet again, held my breath, and launched it.
Exact same thing.
A bit more disheartened now, I kept trying. I decided to focus on the keyboard input problem specifically, since I wasn't any closer to figuring out the graphical one. And keep in mind, this code is still completely obfuscated, you have no idea what does what, you only managed to get it to compile again. So I traced and traced, sending debug output here and there, finally working my way back to a function again which I'd looked at to start with and dismissed. It wasn't being called from anywhere, and I'd initially assumed it was unused code, but that made less sense after I found my way back to it again.
Turns out, it was the crucial code. The decompiler had interpreted the bytecode incorrectly, and was never calling it at all! I rewrote this tiny portion to actually execute it, and suddenly the title screen worked. The graphics weren't broken at all, that's just the way it looks the moment before the blocks come flying down to spell the logo! The input worked now as well. This function was obviously crucial to handling timing and input, and it simply wasn't being called. What a relief. I realized that I might could have even avoided decompiling the second time if I'd found this sooner!
So then I did the ultimate test, and started a game. That's when everything truly went to hell. This screenshot came only after I'd stopped frequent crashing by commenting out things like entity updates.
This one was the disaster. It was hard to tell where to go because nothing was labeled. I disable various things just to see if it even made a visual impact on the game to know if I needed to go deeper there or not. Eventually I came to the conclusion that there probably was just no fixing this. Throughout the process so far I'd come to realize that the issue with so many of the obfuscated names being the same, only differentiated by their arguments, was the death of it. They could be littered throughout the code, calling the completely wrong functions, yet not throw any compile errors because technically the arguments are correct.
An example of this might be one function that wants three integers, and another function that wants three floating-point values. In Java you can pass an integer to a floating point function and it won't complain. But the problem is that if both functions have the same name, then the wrong one is going to get called if you pass those integers but were wanting the floating-point one. Java doesn't have this problem normally because the compiler tracks this stuff. But when you're going in reverse it's up to the decompiler to figure this out, which is much trickier. And mine wasn't doing it.
I dug through and fixed some conflicts in the rendering code which I was able to find, but it made no difference in the end. There was too much code, I could never go through every single class and match up every single argument type of every function call to make sure they were correct. That would take me days, or weeks. I figured that was probably the end of that little project!
But since I'm stubborn, I later decided to look for a different decompiler instead. I tried a few, some seemed to have better results than others. But then I decided I'd give Fernflower a try. It's the one that MCP uses, so I figured if it works for them, maybe it can work for me, even if I wouldn't have the patches and other tools they use when you set MCP up. I honestly didn't know what to expect.
Fernflower has a lot of options, and it took finding the right combination until I got code produced that I felt I could work with. Fernflower was doing some great things, like actually recognizing the arguments a function needed and trying its best to match things up. I was pretty optimistic, especially given that there were far less errors to fix in the code than in any of my previous attempts. I still had to do some manual bytecode comparisons for the occasional issue, but the significant amount of it was easy but tedious fixes. I was able to use some of my old code again for things I knew wouldn't cause issue, which also helped.
So then that magical moment came again, when there were no errors. And I ran it. Everything worked on the title screen, first try. Awesome. My faith in Fernflower skyrocketed. So I did the ultimate test of trying to create a new world. And that's when it just froze, with my computer practically seizing up as well. When I eventually got the task closed, I tried again, but only to the same incredibly strange result. It was absolutely killing my computer, pegging both cores (I only have a wimpy dual-core). This was far from the result I expected.
Well here we go again, I thought. This time though I suspected a code loop, so I put it through the debugger, waited until the game got stuck, and paused execution (after my computer caught up with my mouse clicks again). It didn't take long to trace my way to what was indeed a code loop. Fernflower had misinterpreted some bytecode slightly differently than it was intended. I fixed that, and then the amazing thing happened.
It ran. It created a new world. I believe there was possibly one more crash after this, related to chunk sorting or something, which I was able to resolve (with code that didn't match the bytecode, oddly enough, but that made sense in terms of Java). I just sat there and played it for a few minutes, because I couldn't believe I'd finally made the damn thing work.
I didn't have any sound, though. This was a known problem with alpha 1.1.2. So I went and grabbed the a1.1.2_01 jar from my MultiMC, did a file compare on all the class files, and pulled out only the ones that were different. That was only about three or four files maybe. I disassembled and decompiled these, added those very few bits of changed code into the game (including the version labels), and ran it again. Sound worked. Everything worked. Cool beans.
Even though absolutely nothing was labeled in the code yet, I'd found where the version string is printed on the screen when doing the sound patch, so I put my own little tag into the game as my first modification.
After saving my work thus far in case of terrible disaster, I immediately set forth deobfuscating the code. It's like a puzzle; you know what the final thing is supposed to look like, you just have to figure out what each piece is. And there's a lottt of pieces. But, again, like a puzzle, when you figure out some pieces, other pieces fall into place. It can be pretty addicting, and I'd forgotten that feeling from back when I decompiled games in the past. You want to find the next one, and the next one. Just figure one more class, then you'll quit for the time being. At least, until that one kind of makes another one make sense, then you need to figure that one out before you lose your place.
Some of this time is also spent changing things while you're in-game to figure out what they do (which Java can do, code hot-swapping, it's great), so you're also having fun playing the game while poking its guts.
In the last couple days I've deobfuscated a decent amount of the code, but still not even half of it. I at least covered the major things, including most of the blocks and items. It's enough to start doing some basic modifications though.
If you're not familiar with that version then you might not notice right away, but that is indeed 9 pieces of pork stacked! I limited the stack to 16 to compromise a bit, and it'll affect both raw and cooked pork since they share the same base class. Now that I think about it, it should affect everything else that uses that class too, like bread and apples, which is also fine! It might seem like an insignificant change to the game, but to me this was the goal, which I somewhat surprised myself by achieving after all of the failure along the way.
Anyhoo, I realize that this is a really long post, and might not even be particularly interesting to a lot of people. But I thought I'd share this with everyone, in case it helps them have a bit of insight into this process as well. Or maybe it just makes you want to play an old version of Minecraft again, which is fine too!
Just keep in mind that this isn't all the MCP team does. All I did was basically decompile and deobfuscate the game (or part of it, so far). They not only deobfuscate it, but also create all of the mappings and the process to re-obfuscate again, which is a crucial part to being able to release mods! I would like to attempt this as well, but it was only briefly before starting to write this post that I realized that the first version of MCP ever released was, ironically enough, for Minecraft v1.1.2_01. Had I realized, I might have simply downloaded that, even, and saved you from this terribly long post.
Maybe I'll leave reobfuscation to the experts!
Sounds like you had fun. I would recommend putting something in the title to make it easier to tell that it's a story / recount though, otherwise, pretty interesting. Nice work! Now here's a challenge, try coding your own ways to do things in the current versions, like hunger, experience, etc etc etc. I personally would like to see what you can come up with, and the code probably would look pretty interesting. Good luck!
Rollback Post to RevisionRollBack
Author of the Clarity, Serenity, Sapphire & Halcyon shader packs for Minecraft: Java Edition.
Sounds like you had fun. I would recommend putting something in the title to make it easier to tell that it's a story / recount though, otherwise, pretty interesting. Nice work! Now here's a challenge, try coding your own ways to do things in the current versions, like hunger, experience, etc etc etc. I personally would like to see what you can come up with, and the code probably would look pretty interesting. Good luck!
Sounds like you had fun. I would recommend putting something in the title to make it easier to tell that it's a story / recount though, otherwise, pretty interesting. Nice work! Now here's a challenge, try coding your own ways to do things in the current versions, like hunger, experience, etc etc etc. I personally would like to see what you can come up with, and the code probably would look pretty interesting. Good luck!
Probably a good idea on the title!
Honestly I don't know about adding hunger. If I did, it might be implemented differently. Part of what made pre-beta 1.8 so different is that you couldn't just hide in a corner and heal. You had to carry food around, worsened by the fact that food didn't stack. You couldn't sprint away from a fight, and the entire game was visually darker at night and in caves. So every single skeleton you came across was potentially your end. Mobs were actually scary! Today they just come across as more of an annoyance or an obstacle in many cases, in my opinion at least.
If I were to do XP I'd probably just give it to you upon killing whatever it is instead of picking up orbs. There's a slight bit of annoyance to having to wait that brief moment to make sure you picked it all up before running along after making your kill.
One thing the game really does need is a way to cheat in items! There was no console in singleplayer back then, so you can't give yourself items. For testing and further deobfuscating purposes this might be one of the next things I look into adding.
Probably a good idea on the title! Honestly I don't know about adding hunger. If I did, it might be implemented differently. Part of what made pre-beta 1.8 so different is that you couldn't just hide in a corner and heal. You had to carry food around, worsened by the fact that food didn't stack. You couldn't sprint away from a fight, and the entire game was visually darker at night and in caves. So every single skeleton you came across was potentially your end. Mobs were actually scary! Today they just come across as more of an annoyance or an obstacle in many cases, in my opinion at least. If I were to do XP I'd probably just give it to you upon killing whatever it is instead of picking up orbs. There's a slight bit of annoyance to having to wait that brief moment to make sure you picked it all up before running along after making your kill. One thing the game really does need is a way to cheat in items! There was no console in singleplayer back then, so you can't give yourself items. For testing and further deobfuscating purposes this might be one of the next things I look into adding.
I do have to agree that, even without the enhanced pathfinding mobs had back then, pre-1.8-beta versions were more difficult as you didn't have these other things. But what I meant by making the features in current versions, is to do pretty much what you said, make things difficult. For instance, instead of hunger being the way it is, make hunger affect wider functions than it already does, for example, if you're hungry, you will mine slower, walk slower, and generally be more weary because of how low in energy you are, and eventually the player will be able to die of starvation. You could leave out the feature where when you're practically full on hunger, you start to heal up, and you can substitute the older function of food for another feature. For sprinting, you can maybe relate this to hunger, where the more you sprint and run around, the hungrier you get, but you could also make it so sprinting tires you out and the longer you sprint, the slower you get over time, unless you let yourself rest for a short while. You could even change up experience to be like what you want. One thing I loved about alpha versions is how peaceful the game did look, the green of the grass and leaves did give the game a pleasant atmosphere and environment to survive in. What would be cool is if you maybe changed up things, made days stay like how they were in alpha and make night more scary, perhaps barely giving the player the ability to see anything. And then during nights, you could manipulate torches to basically give off a more warming glow.
I came across a fun bit of code which has been removed now for who knows how long. Part of the sponge block's code still exists in this version. It loops through a two-block radius on each axis and checks for water blocks. The inner part of the loop has been removed, likely just the function call to remove those water blocks from the world has been commented out. Breaking the sponge does a similar loop in that radius, but also still carries out its function of triggering block updates to all of its neighbors, to encourage water to flow back into the space.
Keep in mind if you never played Classic mode that sponges were a big deal when they were added. Water mechanics were completely different at the time. Entire water source blocks flowed horizontally and downward to fill in any empty space. Liquid could, in fact, consume the entire world if it were placed high enough above the terrain. All you could do back then was block off the water and fill the entire space in with blocks if you accidentally flooded an area (or if someone trolled your server). The sponge saved you from that, by simply destroying all water blocks in its vicinity. You could use it as a dam, spaced around an area to ensure water never came past.
It might be fun to re-enable its water-destroying behavior, and add a crafting recipe of like four wool. Especially since wool had pretty much no other use in this version!
I confirmed the code being removed as long ago as Release 1.2.5, though I don't have any versions of MCP handy prior to that to see when exactly it was taken out.
That might help you peg it down. But damn man i'm having fun reading your progress.
Cool, glad there's other folks interested in this kind of stuff too.
I'd have to run MCP on various versions of Minecraft to track down when that sponge code was last included, which I may still do eventually, but it's not super important.
I did re-implement the sponge, though! I had to actually find the crafting recipes class first, which also prompted me to finish identifying and labeling all the game items. But I added the recipe as four wool. Then I added the line to the sponge class to delete the water blocks in that code loop that's still left.
Graphical glitch there due to a lack of block update. If I sit dirt down nearby it refreshes properly and you see the appropriate edges. So I added in the code to automatically trigger neighbor block updates, but that had the unintentional but expected result of re-triggering the water flow. So I'd sit the sponge down, the water would get deleted around it, but then it would immediately flow and regenerate again. I imagine the original water blocks rendered differently, especially since they didn't have varying heights, so none of this was a problem for them, and it didn't need the block updates at all.
So I had to make sure I limited the range in which the block updates happened, which corrected it for the most part, but still left some minor visual glitches on some water blocks farther out of range. You can see an example near the crosshair.
But it works, so I'll leave it!
I also implemented a toggle of F3 as I mentioned in the original post, instead of having to hold it. And I added the much-needed player coordinates to that info, as seen in the screenshot.
Probably the best feature though, since you can't use /time, is that I needed a way to get through the night, especially if I want to take screenshots or test something without worrying about dying. So I can now hold down F4, and the world time updates 10x faster. Entities and everything else still function properly in regular time, only the world time is accelerated. It's kinda neat to watch the moon zip across the sky.
Could you put up a download of that forge funzies.?
Also this is very interesting. You mentioned immibis using the very old method instead of going thorugh MCP, I just wanted to point out that podzol to diamonds is not the only mod using this method circumventing MCP. Optifine believe it or not usings this method, but with the assitance of MCP.. How do you think there is a 1.7.4 version of optifine, and only a 1.7.2 realease of MCP out. sp14x is pretty damn smart.
I only wish i could do something like this. my knowlege stops after registering a mod.
Been there. Every modder probably has. There isn't really an easy way to learn, especially when it keeps changing. I'd probably suggest just using MCP rather than Forge so that you can poke around at Minecraft however you want without having to try to learn all of Forge's behaviors on top of it. Eventually you figure out how the game works and it snowballs a bit.
If it weren't for my existing knowledge of how Minecraft works, I don't know if I'd be anywhere near this far yet on this project.
Could you put up a download of that forge funzies.?
Also this is very interesting. You mentioned immibis using the very old method instead of going thorugh MCP, I just wanted to point out that podzol to diamonds is not the only mod using this method circumventing MCP. Optifine believe it or not usings this method, but with the assitance of MCP.. How do you think there is a 1.7.4 version of optifine, and only a 1.7.2 realease of MCP out. sp14x is pretty damn smart.
I can't really release anything yet, it's a completely deobfuscated JAR at the moment, and Mojang doesn't like people handing those out.
You're right about the Optifine developer though, I forgot about that. Some of the classes he affects are rather large, so he probably is able to find them right away whenever a new version comes out. I was able to visually identify the block renderer class in this old version almost right away myself, which is something I know he definitely modifies. I'm not sure what else Optifine changes these days though, so some of his edits might be a bit harder!
Really impressed with how far down the rabbit hole you went with this! I started along a similar path when I got into modding MC for the first time, but I would say I stopped about halfway through where you got in your story because I realized just how obnoxious a task it would be to clear up the plentiful bytecode ambiguity artifacts produced by the decompiler.
So anyway, congrats man on making it all the way to the end with this adventure! Not many people (especially on this forum) could have gotten as far. It makes me appreciate what the MCP and Forge team do even more.
Also if memory serves wasn't Red Power initially built this way before Eloraam ported to forge?
No it used MCP all the way through, she obfuscated, her classes with MCP just like everyone else, yet attempted to keep her mod closed source, I always found it quite funny. Spacetoad and Eloraam created forge because of a need to edit the same classes. Spacetoad also wanted a way to add custom buckets to the original minecraft bucket, but do that requires editing a lot of vanilla classes. In order to solve these problems eloraam, and spacetoad created forge, originally just a simple thing that allowed there mods to be compatible, but they released forge to the public, and said others could use it, and it grew to be really, really, really big.
Another point Redpower or buildcraft were never "ported" to forge, its ironically the other way around, forge was "ported" to redpower and buildcraft so that it may work with them, in doing that buildcraft and redpower began to require forge (and at the time forge wasn't so clean, forge itself required modloader, and modloaderMP) - the newly created forge team, created FML or forge modloader which is natively included in all forge downloads to circumvent to need for both modlader, and eventually modloaderMP, minecraft then merged SP and MP worlds, so the need for separate modloaders was out of the question, this made all mods that used forge completely compatible with both SSP and SMP.
MCP is a great modding tool, and forge is a great modding addition. CHERISH them, but let's not forget about older APIS, not modloader, and modloaderMP while we're at it.
I thought people might like to get an idea of how much code we're talking about, even in this really old version of the game. Have a look at my directory listing screenshot. The main Minecraft class is in that "client" directory, too, which is a pretty important piece.
Anything you see there with one or two letters in the filename is something I either haven't figured out or simply haven't gotten around to figuring out yet. There might be a few there with just preliminary names as well until I confirm their purpose further. Those are just the class names, even, and each one of those files contains various methods and variables! Just because I figure out the class doesn't mean I know everything it's doing yet.
And this is with every item, block, mob, particle, tile entity, etc, figured out. There's probably more than enough to make some good mods, even. But there's still quite a lot of important code which needs to be deciphered before I'm satisfied.
Really impressed with how far down the rabbit hole you went with this! I started along a similar path when I got into modding MC for the first time, but I would say I stopped about halfway through where you got in your story because I realized just how obnoxious a task it would be to clear up the plentiful bytecode ambiguity artifacts produced by the decompiler.
So anyway, congrats man on making it all the way to the end with this adventure! Not many people (especially on this forum) could have gotten as far. It makes me appreciate what the MCP and Forge team do even more.
Thanks! If it weren't for there being better decompilers than what I started with then I may have ended up doing exactly as you did. The thought crossed my mind of trying to write a tool to interpret the method signatures from the disassembly listings (from javap) to assist me in fixing them all, but I still don't know if I would have bothered. If I'd gone to that much trouble and then found yet more problems as a result of that decompiler then I would have been pretty disappointed!
I honestly have no memory of this functionality, nor of a webpage on Mojang's site which would let you do it, as suggested by the wiki page. But that was a long time ago, so who knows what fell out of my head after all this time.
Obviously the code and applet still exist in this old version of Minecraft, even though you have to run it standalone. It doesn't seem to have any way to access it in-game. Pretty neat though!
So today, after having to deobfuscate quite a lot of convoluted inventory-related code, I was able to reveal enough to make a much-needed change: shift-clicking items back into your inventory. The other thing I added was a tiny change to make crafting tables be affected by the axe like in modern versions.
The shift-clicking thing wasn't a lot of code. It was practically just half a dozen lines, as a matter of fact. Basically it just takes whatever you shift-click on, remove it from the inventory, then re-add it to the player's (which took some tinkering to prevent completely destroying items). The downside is that I can't shift-click items from the player inventory into another inventory, such as a chest. Yet. The player inventory class has quite a lot more functionality for handling taking and putting items which other inventory classes don't, so I'll have to re-implement some of that functionality to work generically on any inventory, more like modern Minecraft does I believe. Either way, just being able to shift-click things back into my inventory is a big improvement!
EDIT: Actually I went ahead and whipped that wrapper class up, so now I can use the easier functionality of the player inventory with any other inventory. Then a minor bit of changing the code now lets me shift-click items in and out of chests!
That whole shift-clicking items in inventories to move things ended up with a few more problems discovered after that post, but I've resolved all of them now. One was an accidental bug as well, where I was making the item stack as large as the durability before I fully realized some of the variables, which resulted in basically automatically repairing tools and giving you entire stacks worth!
The two-way functionality (sending items from one on-screen inventory to another) works for all the inventory GUIs now as well, from furnaces, to chests, even crafting tables. It's handy to quickly shift-click items up into a furnace, for example, like ore followed by coal. Shift-clicking crafted items from the crafting table is also much handier and more like the modern game, even though I didn't go all the way and implement the craft-stacks-till-out-of-resources aspect. It just pulls out one recipe result at a time, which is fine with me because it prevents those occasional accidents where you craft a bunch of junk items.
After that though I added a whole bunch of things! The axe now affects the crafting table, the pickaxe affects the furnace and redstone ore. This is why they always broke so slowly in the old versions, because they just weren't in the list of items that those tools could damage.
Another important one is holding shift when placing blocks. We take that for granted today, but it was a real problem back then. You couldn't sit a block against anything that could be activated or you'd always bring up its GUI. You had to put a block near it and right-click that block instead. But I resolved that!
Wasting food. It's a really annoying thing, but the game would let you do it. Even if you had full health you'd consume it anyway. So I added in a little check to prevent you from using it if your health is full.
The furnace didn't drop its inventories back then. I remember the first time I ever realized this was when it had a big stack of iron and coal in it, which was completely destroyed. I added that code in now too, basically copying how the chest works.
The last couple things I've added are mostly just improvements. One is something that InventoryTweaks does. When a tool breaks, it automatically replaces it with another from your inventory. I always found this to be a really valuable feature, especially if you're in the middle of a fight and your sword wears out. Due to the way the game is designed I could only implement this in certain circumstances, and even that's a bit of a hack. But it does work any time a tool breaks when hitting an entity or breaking a block. I haven't tested it, but I imagine that the flint & steel, hoes, etc, wouldn't get replaced since those perform other actions.
The other improvement is torch placement. It's silly to have to constantly switch to torches while you're spelunking, especially in this old version where everything is much darker and torch light doesn't carry as far. So I made middle-click place torches for you, as long as they're on your hotbar. I left in the other functionality of selecting the same block as whatever you're looking at, you just have to hold shift while middle-clicking now to do it.
I think that's about it. Well, other than adding in the glass breaking sound when a tool breaks (since there's no tool breaking sound in this version), which I did because otherwise you might not realize it when your tool is automatically replaced with another from your inventory.
This old version of the game is getting much more enjoyable to play. Maybe I really should look into how to reobfuscate my code somehow, because I think other people might enjoy this stuff too!
Couple things added today, which will help in any other additions or testing I might want to do.
First is a singleplayer console. I only wrote in two commands so far, /time and /seed, but being able to give myself blocks through it is also on the todo list. The time command pretty much makes my F4 time acceleration useless, albeit still fun to watch. The seed command was added afterward, when I needed to test something else later.
After adding the /time command, I was able to witness something which is otherwise impossible, unless the server back then also had a /time command (which I'd have to find a server for that version to find out). In this old version, the lighting worked a lot differently. As the time changes towards sunset and daybreak, you have this rolling effect as the chunks re-render away from you with the updated skylight brightness. I always kind of liked the effect, actually. But it happens somewhat gradually. Well, since I can change between afternoon to the dead of night with some keystrokes, I can cause a drastic change in the skylight value, causing the rolling update effect to be much more noticeable. I grabbed a screenshot mid-change:
Pretty neat to see! You can also see a bit of the console at work there. But keep in mind that I literally just ripped off the multiplayer chat interface to do it. This has the unfortunate side effect of everything vanishing from the screen after a few seconds, gone into the void, unlike how the modern console works with a buffer and such. But it's better than nothing!
In case you're wondering, beta 1.8 changed the lighting system to use that more modern dynamic system we have today, where chunks don't actually have to be re-rendered to reflect the sky light change. It's handled through a second texture unit and a texture of the game's light spectrum, I believe. This also allows for the other little touches, like torch light flickering subtly. That would be impossible without constant render updates prior to that. Changing block light values still requires a render update, though, even today.
So then I added something else.
This GUI doesn't normally exist. Normally when you pick an empty world slot it just immediately creates a new world with no options to change. So I added a few!
Keep in mind that this version was before biomes. However, there's a 1 in 4 chance that the world will be flagged as "snow covered" upon creation. This means it snows, all the time, and everything's covered. When I'm testing things I don't particularly care for those worlds, especially for how commonly they show up. So I designed this GUI initially for that purpose alone.
The "World Type" button switches between "Random", "Summer", and "Winter". And then of course there's the seed field that I added afterward, but it turns out that specifying your seed is pretty useless in this version, unfortunately! I confirmed that the proper seed was being transferred to the world creation and everything, as well as being able to use /seed to see what the world was created with, but apparently the worldgen is just so different in this version that it relies on general randomness a lot more than the more predictable perlin noise methods as modern Minecraft does. I'm fairly certain this version still does use perlin noise for some aspects of worldgen, but I haven't figured all the details of that out yet.
Anyhow, that's about it, other than a bit more deobfuscation. Oh, I also fixed a bug in some of the GUIs, where whenever you resize them it re-adds all their buttons again, which can cause a mess of duplicates. I just set those specific GUIs to clear the button list each time. I'd noticed this issue before, actually, but it wasn't until I added my own GUI today that I wanted to figure out how to fix it for good.
We all enjoy modding today thanks to the work of the MCP and Forge teams, but there was obviously a time when there was no MCP and Forge. People had to manually disassemble/decompile the various classes and try to figure out what did what so that they could write even the simplest JAR mods. I believe there may still be a handful of people who try to do this now, like Immibis, who makes mods like Podzol to Diamonds not long after a Minecraft version gets released. But overall I think that kind of modding is mostly a thing of the past.
Well, I decided I wanted to see if I could do this myself. But not just write a simple mod; I wanted to be able to do anything to the game, regardless of whether it was releasable. And I wanted to do it to a specific version. In other words, I wanted to decompile and deobfuscate the game, by myself.
The subject/victim of my project was alpha 1.1.2. It was the last version before biomes were introduced, had a noticeably different look and feel, and obviously the simplest of all the versions I like to play with. It also had specific characteristics that I wanted to try to mod right away, such as making food stack, adding your coordinates into the F3 info, making the F3 info toggle on and off instead of only showing when you hold the key, burning wood tools, creating charcoal from logs, shift-click items from the crafting table, etc. Mostly modern conveniences, for starters.
Well since Mojang obfuscates their code, it makes it tricky to decompile. It not only randomizes all of the methods, variables, and class names, but it adds in junk bytecode and such into the compiled class files to confuse any automated programs trying to decompile them back into Java. I figured I would be able to deal with this, because I've decompiled DOS games before (such as Legend of the Red Dragon) and managed to thoroughly label their guts, and the mess of assembly code you initially get from doing that didn't seem like it would be much different than this. Reconstructing Java is a hundred times easier than reconstructing C or Pascal, because no matter how much you try to obfuscate it you're still going to leave behind certain bits of information due to the nature of the language. Plus, I already have Minecraft modding experience, so I had an idea of what goes on inside the game already.
The mistake of course is to ever assume something will be easy.
Minecraft is huge. Even ancient alpha 1.1.2 is a significant amount of code. When I decompiled it (using JD-GUI), I was faced with hundreds upon hundreds of Java errors to fix. This was expected, and many of these weren't hard, sometimes fixing half a dozen in one swoop with some of the automation of Eclipse (a great Java editor). But there were some that I just didn't understand. It was trying to pass arguments to functions which didn't take those arguments, and just a myriad of other conflicting and confusing things in the code.
The biggest problem is that obfuscation causes tons and tons of name clashes in the code, such as multiple functions simply named "a", even inside the same class, where only the arguments passed to that function determine the proper function to be called. This can be a nightmare to deal with.
I went through this code by hand and tried to fix it to the best of my ability. But at some point, as I learned the process better, I realized I'd probably made mistakes earlier on. I'd come up with a better method of resolving conflicts, by looking directly in the disassembled bytecode listing instead of digging through the decompiled Java, and using that as the reference to find out the proper argument types to pass to a function. Slower, yet far more reliable. But it was too late to go back, there was no way to know where the mistakes would be. All I could do was keep going forward, and hope I could fix it once I had something that ran.
This entire process took hours, mind you.
The moment the error count dropped to 0 was a happy time. I had questioned whether I'd even get that far after all the issues I ran into. So, I ran it, and it didn't crash!
Broken. And not only visually, but it wouldn't respond to mouse clicks, either. I wasn't entirely surprised, so I set out to try to fix it. I dug around and dug around, for quite some time, until I eventually said screw it. I'd messed this up most likely, and I was never going to find my mistakes.
So I started over.
It went faster this time. I was able to copy some of my work from the previous attempt, files which I knew I'd done properly. It still took quite some time, but I eventually knocked all the errors out yet again, held my breath, and launched it.
Exact same thing.
A bit more disheartened now, I kept trying. I decided to focus on the mouse input problem specifically, since I wasn't any closer to figuring out the graphical one. And keep in mind, this code is still completely obfuscated, you have no idea what does what, you only managed to get it to compile again. So I traced and traced, sending debug output here and there, finally working my way back to a function again which I'd looked at to start with and dismissed. It wasn't being called from anywhere, and I'd initially assumed it was unused code, but that made less sense after I found my way back to it again.
Turns out, it was the crucial code. The decompiler had interpreted the bytecode incorrectly, and was never calling it at all! I rewrote this tiny portion to actually execute it, and suddenly the title screen worked. The graphics weren't broken at all, that's just the way it looks the moment before the blocks come flying down to spell the logo! The input worked now as well. This function was obviously crucial to handling timing and input, and it simply wasn't being called. What a relief. I realized that I might could have even avoided decompiling the second time if I'd found this sooner!
So then I did the ultimate test, and started a game. That's when everything truly went to hell. This screenshot came only after I'd stopped frequent crashing by commenting out things like entity updates.
This one was the disaster. It was hard to tell where to go because nothing was labeled. I disable various things just to see if it even made a visual impact on the game to know if I needed to go deeper there or not. Eventually I came to the conclusion that there probably was just no fixing this. Throughout the process so far I'd come to realize that the issue with so many of the obfuscated names being the same, only differentiated by their arguments, was the death of it. They could be littered throughout the code, calling the completely wrong functions, yet not throw any compile errors because technically the arguments are correct.
An example of this might be one function that wants three integers, and another function that wants three floating-point values. In Java you can pass an integer to a floating point function and it won't complain. But the problem is that if both functions have the same name, then the wrong one is going to get called if you pass those integers but were wanting the floating-point one. Java doesn't have this problem normally because the compiler tracks this stuff. But when you're going in reverse it's up to the decompiler to figure this out, which is much trickier. And mine wasn't doing it.
I dug through and fixed some conflicts in the rendering code which I was able to find, but it made no difference in the end. There was too much code, I could never go through every single class and match up every single argument type of every function call to make sure they were correct. That would take me days, or weeks. I figured that was probably the end of that little project!
But since I'm stubborn, I later decided to look for a different decompiler instead. I tried a few, some seemed to have better results than others. But then I decided I'd give Fernflower a try. It's the one that MCP uses, so I figured if it works for them, maybe it can work for me, even if I wouldn't have the patches and other tools they use when you set MCP up. I honestly didn't know what to expect.
Fernflower has a lot of options, and it took finding the right combination until I got code produced that I felt I could work with. Fernflower was doing some great things, like actually recognizing the arguments a function needed and trying its best to match things up. I was pretty optimistic, especially given that there were far less errors to fix in the code than in any of my previous attempts. I still had to do some manual bytecode comparisons for the occasional issue, but the significant amount of it was easy but tedious fixes. I was able to use some of my old code again for things I knew wouldn't cause issue, which also helped.
So then that magical moment came again, when there were no errors. And I ran it. Everything worked on the title screen, first try. Awesome. My faith in Fernflower skyrocketed. So I did the ultimate test of trying to create a new world. And that's when it just froze, with my computer practically seizing up as well. When I eventually got the task closed, I tried again, but only to the same incredibly strange result. It was absolutely killing my computer, pegging both cores (I only have a wimpy dual-core). This was far from the result I expected.
Well here we go again, I thought. This time though I suspected a code loop, so I put it through the debugger, waited until the game got stuck, and paused execution (after my computer caught up with my mouse clicks again). It didn't take long to trace my way to what was indeed a code loop. Fernflower had misinterpreted some bytecode slightly differently than it was intended. I fixed that, and then the amazing thing happened.
It ran. It created a new world. I believe there was possibly one more crash after this, related to chunk sorting or something, which I was able to resolve (with code that didn't match the bytecode, oddly enough, but that made sense in terms of Java). I just sat there and played it for a few minutes, because I couldn't believe I'd finally made the damn thing work.
I didn't have any sound, though. This was a known problem with alpha 1.1.2. So I went and grabbed the a1.1.2_01 jar from my MultiMC, did a file compare on all the class files, and pulled out only the ones that were different. That was only about three or four files maybe. I disassembled and decompiled these, added those very few bits of changed code into the game (including the version labels), and ran it again. Sound worked. Everything worked. Cool beans.
Even though absolutely nothing was labeled in the code yet, I'd found where the version string is printed on the screen when doing the sound patch, so I put my own little tag into the game as my first modification.
After saving my work thus far in case of terrible disaster, I immediately set forth deobfuscating the code. It's like a puzzle; you know what the final thing is supposed to look like, you just have to figure out what each piece is. And there's a lottt of pieces. But, again, like a puzzle, when you figure out some pieces, other pieces fall into place. It can be pretty addicting, and I'd forgotten that feeling from back when I decompiled games in the past. You want to find the next one, and the next one. Just figure one more class, then you'll quit for the time being. At least, until that one kind of makes another one make sense, then you need to figure that one out before you lose your place.
Some of this time is also spent changing things while you're in-game to figure out what they do (which Java can do, code hot-swapping, it's great), so you're also having fun playing the game while poking its guts.
In the last couple days I've deobfuscated a decent amount of the code, but still not even half of it. I at least covered the major things, including most of the blocks and items. It's enough to start doing some basic modifications though.
If you're not familiar with that version then you might not notice right away, but that is indeed 9 pieces of pork stacked! I limited the stack to 16 to compromise a bit, and it'll affect both raw and cooked pork since they share the same base class. Now that I think about it, it should affect everything else that uses that class too, like bread and apples, which is also fine! It might seem like an insignificant change to the game, but to me this was the goal, which I somewhat surprised myself by achieving after all of the failure along the way.
Anyhoo, I realize that this is a really long post, and might not even be particularly interesting to a lot of people. But I thought I'd share this with everyone, in case it helps them have a bit of insight into this process as well. Or maybe it just makes you want to play an old version of Minecraft again, which is fine too!
Just keep in mind that this isn't all the MCP team does. All I did was basically decompile and deobfuscate the game (or part of it, so far). They not only deobfuscate it, but also create all of the mappings and the process to re-obfuscate again, which is a crucial part to being able to release mods! I would like to attempt this as well, but it was only briefly before starting to write this post that I realized that the first version of MCP ever released was, ironically enough, for Minecraft v1.1.2_01. Had I realized, I might have simply downloaded that, even, and saved you from this terribly long post.
Maybe I'll leave reobfuscation to the experts!
WIP site for my mods / Intermediary / FMC / Redstone Paste / Hopper Ducts / Model Citizens / Simple Refinement / Endermanage / Fycraft / etc
Sounds like you had fun. I would recommend putting something in the title to make it easier to tell that it's a story / recount though, otherwise, pretty interesting. Nice work! Now here's a challenge, try coding your own ways to do things in the current versions, like hunger, experience, etc etc etc. I personally would like to see what you can come up with, and the code probably would look pretty interesting. Good luck!
Author of the Clarity, Serenity, Sapphire & Halcyon shader packs for Minecraft: Java Edition.
My Github page.
The entire Minecraft shader development community now has its own Discord server! Feel free to join and chat with all the developers!
you do realize Fyber has several mods, right?
Probably a good idea on the title!
Honestly I don't know about adding hunger. If I did, it might be implemented differently. Part of what made pre-beta 1.8 so different is that you couldn't just hide in a corner and heal. You had to carry food around, worsened by the fact that food didn't stack. You couldn't sprint away from a fight, and the entire game was visually darker at night and in caves. So every single skeleton you came across was potentially your end. Mobs were actually scary! Today they just come across as more of an annoyance or an obstacle in many cases, in my opinion at least.
If I were to do XP I'd probably just give it to you upon killing whatever it is instead of picking up orbs. There's a slight bit of annoyance to having to wait that brief moment to make sure you picked it all up before running along after making your kill.
One thing the game really does need is a way to cheat in items! There was no console in singleplayer back then, so you can't give yourself items. For testing and further deobfuscating purposes this might be one of the next things I look into adding.
WIP site for my mods / Intermediary / FMC / Redstone Paste / Hopper Ducts / Model Citizens / Simple Refinement / Endermanage / Fycraft / etc
I do have to agree that, even without the enhanced pathfinding mobs had back then, pre-1.8-beta versions were more difficult as you didn't have these other things. But what I meant by making the features in current versions, is to do pretty much what you said, make things difficult. For instance, instead of hunger being the way it is, make hunger affect wider functions than it already does, for example, if you're hungry, you will mine slower, walk slower, and generally be more weary because of how low in energy you are, and eventually the player will be able to die of starvation. You could leave out the feature where when you're practically full on hunger, you start to heal up, and you can substitute the older function of food for another feature. For sprinting, you can maybe relate this to hunger, where the more you sprint and run around, the hungrier you get, but you could also make it so sprinting tires you out and the longer you sprint, the slower you get over time, unless you let yourself rest for a short while. You could even change up experience to be like what you want. One thing I loved about alpha versions is how peaceful the game did look, the green of the grass and leaves did give the game a pleasant atmosphere and environment to survive in. What would be cool is if you maybe changed up things, made days stay like how they were in alpha and make night more scary, perhaps barely giving the player the ability to see anything. And then during nights, you could manipulate torches to basically give off a more warming glow.
I honestly don't see the relevance your message has to mine at all, as I was talking about what he did in this recount, not his mods.
Author of the Clarity, Serenity, Sapphire & Halcyon shader packs for Minecraft: Java Edition.
My Github page.
The entire Minecraft shader development community now has its own Discord server! Feel free to join and chat with all the developers!
Keep in mind if you never played Classic mode that sponges were a big deal when they were added. Water mechanics were completely different at the time. Entire water source blocks flowed horizontally and downward to fill in any empty space. Liquid could, in fact, consume the entire world if it were placed high enough above the terrain. All you could do back then was block off the water and fill the entire space in with blocks if you accidentally flooded an area (or if someone trolled your server). The sponge saved you from that, by simply destroying all water blocks in its vicinity. You could use it as a dam, spaced around an area to ensure water never came past.
It might be fun to re-enable its water-destroying behavior, and add a crafting recipe of like four wool. Especially since wool had pretty much no other use in this version!
I confirmed the code being removed as long ago as Release 1.2.5, though I don't have any versions of MCP handy prior to that to see when exactly it was taken out.
WIP site for my mods / Intermediary / FMC / Redstone Paste / Hopper Ducts / Model Citizens / Simple Refinement / Endermanage / Fycraft / etc
That might help you peg it down. But damn man i'm having fun reading your progress.
[Don't let the offline notice fool you.]
Cool, glad there's other folks interested in this kind of stuff too.
I'd have to run MCP on various versions of Minecraft to track down when that sponge code was last included, which I may still do eventually, but it's not super important.
I did re-implement the sponge, though! I had to actually find the crafting recipes class first, which also prompted me to finish identifying and labeling all the game items. But I added the recipe as four wool. Then I added the line to the sponge class to delete the water blocks in that code loop that's still left.
Graphical glitch there due to a lack of block update. If I sit dirt down nearby it refreshes properly and you see the appropriate edges. So I added in the code to automatically trigger neighbor block updates, but that had the unintentional but expected result of re-triggering the water flow. So I'd sit the sponge down, the water would get deleted around it, but then it would immediately flow and regenerate again. I imagine the original water blocks rendered differently, especially since they didn't have varying heights, so none of this was a problem for them, and it didn't need the block updates at all.
So I had to make sure I limited the range in which the block updates happened, which corrected it for the most part, but still left some minor visual glitches on some water blocks farther out of range. You can see an example near the crosshair.
But it works, so I'll leave it!
I also implemented a toggle of F3 as I mentioned in the original post, instead of having to hold it. And I added the much-needed player coordinates to that info, as seen in the screenshot.
Probably the best feature though, since you can't use /time, is that I needed a way to get through the night, especially if I want to take screenshots or test something without worrying about dying. So I can now hold down F4, and the world time updates 10x faster. Entities and everything else still function properly in regular time, only the world time is accelerated. It's kinda neat to watch the moon zip across the sky.
WIP site for my mods / Intermediary / FMC / Redstone Paste / Hopper Ducts / Model Citizens / Simple Refinement / Endermanage / Fycraft / etc
I miss the good old days.
Also this is very interesting. You mentioned immibis using the very old method instead of going thorugh MCP, I just wanted to point out that podzol to diamonds is not the only mod using this method circumventing MCP. Optifine believe it or not usings this method, but with the assitance of MCP.. How do you think there is a 1.7.4 version of optifine, and only a 1.7.2 realease of MCP out. sp14x is pretty damn smart.
[Don't let the offline notice fool you.]
Been there. Every modder probably has. There isn't really an easy way to learn, especially when it keeps changing. I'd probably suggest just using MCP rather than Forge so that you can poke around at Minecraft however you want without having to try to learn all of Forge's behaviors on top of it. Eventually you figure out how the game works and it snowballs a bit.
If it weren't for my existing knowledge of how Minecraft works, I don't know if I'd be anywhere near this far yet on this project.
I can't really release anything yet, it's a completely deobfuscated JAR at the moment, and Mojang doesn't like people handing those out.
You're right about the Optifine developer though, I forgot about that. Some of the classes he affects are rather large, so he probably is able to find them right away whenever a new version comes out. I was able to visually identify the block renderer class in this old version almost right away myself, which is something I know he definitely modifies. I'm not sure what else Optifine changes these days though, so some of his edits might be a bit harder!
I honestly have no idea on the history of some of the oldest mods (wish I did), but going by what we know of Eloraam, she probably did.
WIP site for my mods / Intermediary / FMC / Redstone Paste / Hopper Ducts / Model Citizens / Simple Refinement / Endermanage / Fycraft / etc
So anyway, congrats man on making it all the way to the end with this adventure! Not many people (especially on this forum) could have gotten as far. It makes me appreciate what the MCP and Forge team do even more.
No it used MCP all the way through, she obfuscated, her classes with MCP just like everyone else, yet attempted to keep her mod closed source, I always found it quite funny. Spacetoad and Eloraam created forge because of a need to edit the same classes. Spacetoad also wanted a way to add custom buckets to the original minecraft bucket, but do that requires editing a lot of vanilla classes. In order to solve these problems eloraam, and spacetoad created forge, originally just a simple thing that allowed there mods to be compatible, but they released forge to the public, and said others could use it, and it grew to be really, really, really big.
Another point Redpower or buildcraft were never "ported" to forge, its ironically the other way around, forge was "ported" to redpower and buildcraft so that it may work with them, in doing that buildcraft and redpower began to require forge (and at the time forge wasn't so clean, forge itself required modloader, and modloaderMP) - the newly created forge team, created FML or forge modloader which is natively included in all forge downloads to circumvent to need for both modlader, and eventually modloaderMP, minecraft then merged SP and MP worlds, so the need for separate modloaders was out of the question, this made all mods that used forge completely compatible with both SSP and SMP.
MCP is a great modding tool, and forge is a great modding addition. CHERISH them, but let's not forget about older APIS, not modloader, and modloaderMP while we're at it.
Anything you see there with one or two letters in the filename is something I either haven't figured out or simply haven't gotten around to figuring out yet. There might be a few there with just preliminary names as well until I confirm their purpose further. Those are just the class names, even, and each one of those files contains various methods and variables! Just because I figure out the class doesn't mean I know everything it's doing yet.
And this is with every item, block, mob, particle, tile entity, etc, figured out. There's probably more than enough to make some good mods, even. But there's still quite a lot of important code which needs to be deciphered before I'm satisfied.
Not even MCP deobfuscates all of it though!
Thanks! If it weren't for there being better decompilers than what I started with then I may have ended up doing exactly as you did. The thought crossed my mind of trying to write a tool to interpret the method signatures from the disassembly listings (from javap) to assist me in fixing them all, but I still don't know if I would have bothered. If I'd gone to that much trouble and then found yet more problems as a result of that decompiler then I would have been pretty disappointed!
Cool, thanks for the history lesson. I always like hearing about things in the "good old days" so to speak.
WIP site for my mods / Intermediary / FMC / Redstone Paste / Hopper Ducts / Model Citizens / Simple Refinement / Endermanage / Fycraft / etc
I honestly have no memory of this functionality, nor of a webpage on Mojang's site which would let you do it, as suggested by the wiki page. But that was a long time ago, so who knows what fell out of my head after all this time.
Obviously the code and applet still exist in this old version of Minecraft, even though you have to run it standalone. It doesn't seem to have any way to access it in-game. Pretty neat though!
WIP site for my mods / Intermediary / FMC / Redstone Paste / Hopper Ducts / Model Citizens / Simple Refinement / Endermanage / Fycraft / etc
The shift-clicking thing wasn't a lot of code. It was practically just half a dozen lines, as a matter of fact. Basically it just takes whatever you shift-click on, remove it from the inventory, then re-add it to the player's (which took some tinkering to prevent completely destroying items). The downside is that I can't shift-click items from the player inventory into another inventory, such as a chest. Yet. The player inventory class has quite a lot more functionality for handling taking and putting items which other inventory classes don't, so I'll have to re-implement some of that functionality to work generically on any inventory, more like modern Minecraft does I believe. Either way, just being able to shift-click things back into my inventory is a big improvement!
EDIT: Actually I went ahead and whipped that wrapper class up, so now I can use the easier functionality of the player inventory with any other inventory. Then a minor bit of changing the code now lets me shift-click items in and out of chests!
WIP site for my mods / Intermediary / FMC / Redstone Paste / Hopper Ducts / Model Citizens / Simple Refinement / Endermanage / Fycraft / etc
The two-way functionality (sending items from one on-screen inventory to another) works for all the inventory GUIs now as well, from furnaces, to chests, even crafting tables. It's handy to quickly shift-click items up into a furnace, for example, like ore followed by coal. Shift-clicking crafted items from the crafting table is also much handier and more like the modern game, even though I didn't go all the way and implement the craft-stacks-till-out-of-resources aspect. It just pulls out one recipe result at a time, which is fine with me because it prevents those occasional accidents where you craft a bunch of junk items.
After that though I added a whole bunch of things! The axe now affects the crafting table, the pickaxe affects the furnace and redstone ore. This is why they always broke so slowly in the old versions, because they just weren't in the list of items that those tools could damage.
Another important one is holding shift when placing blocks. We take that for granted today, but it was a real problem back then. You couldn't sit a block against anything that could be activated or you'd always bring up its GUI. You had to put a block near it and right-click that block instead. But I resolved that!
Wasting food. It's a really annoying thing, but the game would let you do it. Even if you had full health you'd consume it anyway. So I added in a little check to prevent you from using it if your health is full.
The furnace didn't drop its inventories back then. I remember the first time I ever realized this was when it had a big stack of iron and coal in it, which was completely destroyed. I added that code in now too, basically copying how the chest works.
The last couple things I've added are mostly just improvements. One is something that InventoryTweaks does. When a tool breaks, it automatically replaces it with another from your inventory. I always found this to be a really valuable feature, especially if you're in the middle of a fight and your sword wears out. Due to the way the game is designed I could only implement this in certain circumstances, and even that's a bit of a hack. But it does work any time a tool breaks when hitting an entity or breaking a block. I haven't tested it, but I imagine that the flint & steel, hoes, etc, wouldn't get replaced since those perform other actions.
The other improvement is torch placement. It's silly to have to constantly switch to torches while you're spelunking, especially in this old version where everything is much darker and torch light doesn't carry as far. So I made middle-click place torches for you, as long as they're on your hotbar. I left in the other functionality of selecting the same block as whatever you're looking at, you just have to hold shift while middle-clicking now to do it.
I think that's about it. Well, other than adding in the glass breaking sound when a tool breaks (since there's no tool breaking sound in this version), which I did because otherwise you might not realize it when your tool is automatically replaced with another from your inventory.
This old version of the game is getting much more enjoyable to play. Maybe I really should look into how to reobfuscate my code somehow, because I think other people might enjoy this stuff too!
WIP site for my mods / Intermediary / FMC / Redstone Paste / Hopper Ducts / Model Citizens / Simple Refinement / Endermanage / Fycraft / etc
First is a singleplayer console. I only wrote in two commands so far, /time and /seed, but being able to give myself blocks through it is also on the todo list. The time command pretty much makes my F4 time acceleration useless, albeit still fun to watch. The seed command was added afterward, when I needed to test something else later.
After adding the /time command, I was able to witness something which is otherwise impossible, unless the server back then also had a /time command (which I'd have to find a server for that version to find out). In this old version, the lighting worked a lot differently. As the time changes towards sunset and daybreak, you have this rolling effect as the chunks re-render away from you with the updated skylight brightness. I always kind of liked the effect, actually. But it happens somewhat gradually. Well, since I can change between afternoon to the dead of night with some keystrokes, I can cause a drastic change in the skylight value, causing the rolling update effect to be much more noticeable. I grabbed a screenshot mid-change:
Pretty neat to see! You can also see a bit of the console at work there. But keep in mind that I literally just ripped off the multiplayer chat interface to do it. This has the unfortunate side effect of everything vanishing from the screen after a few seconds, gone into the void, unlike how the modern console works with a buffer and such. But it's better than nothing!
In case you're wondering, beta 1.8 changed the lighting system to use that more modern dynamic system we have today, where chunks don't actually have to be re-rendered to reflect the sky light change. It's handled through a second texture unit and a texture of the game's light spectrum, I believe. This also allows for the other little touches, like torch light flickering subtly. That would be impossible without constant render updates prior to that. Changing block light values still requires a render update, though, even today.
So then I added something else.
This GUI doesn't normally exist. Normally when you pick an empty world slot it just immediately creates a new world with no options to change. So I added a few!
Keep in mind that this version was before biomes. However, there's a 1 in 4 chance that the world will be flagged as "snow covered" upon creation. This means it snows, all the time, and everything's covered. When I'm testing things I don't particularly care for those worlds, especially for how commonly they show up. So I designed this GUI initially for that purpose alone.
The "World Type" button switches between "Random", "Summer", and "Winter". And then of course there's the seed field that I added afterward, but it turns out that specifying your seed is pretty useless in this version, unfortunately! I confirmed that the proper seed was being transferred to the world creation and everything, as well as being able to use /seed to see what the world was created with, but apparently the worldgen is just so different in this version that it relies on general randomness a lot more than the more predictable perlin noise methods as modern Minecraft does. I'm fairly certain this version still does use perlin noise for some aspects of worldgen, but I haven't figured all the details of that out yet.
Anyhow, that's about it, other than a bit more deobfuscation. Oh, I also fixed a bug in some of the GUIs, where whenever you resize them it re-adds all their buttons again, which can cause a mess of duplicates. I just set those specific GUIs to clear the button list each time. I'd noticed this issue before, actually, but it wasn't until I added my own GUI today that I wanted to figure out how to fix it for good.
WIP site for my mods / Intermediary / FMC / Redstone Paste / Hopper Ducts / Model Citizens / Simple Refinement / Endermanage / Fycraft / etc