Amazon Ad

Friday, January 12, 2018

Minecraft Modding Tutorial with Kotlin: Setting up Forge with Kotlin, and an Introduction to Kotlin!

Well...it's time to get serious. I'm gonna teach you how to make a mod in a completely different programming language. No java involved.

Some things we need to cover here is what kotlin is. Kotlin is a programming language created by JetBrains to break down the barriers in the computer science industry. Java developers are inside small bubbles. So are C++ developers. Same thing with JavaScript developers. None of them want to explore anything outside of their language (yes I know not all of them do, but for the sake of argumentation, let's just roll with this, but don't assume that every single C++ or Java or JS developer is stubborn and unwilling to change). They'll say stuff like "I don't wanna learn another language. I'm fine with the one I got," or "does that one do x?? Does that one do y??" Kotlin is very hated right now because it challenges the ideas that each of the big name languages push onto developers. C++ has the whole "do everything yourself." Java has the "don't worry fam, I gotchu. Don't worry. Just write code!" C# has the idea that not everything is all about you nor the computer. It's both. The problem with C# is that it is lacking the flare that a language can have nowadays. Language design has been studied for like what 60+ years now? I'm pretty sure we can design a language that has a good read/write flow, has good interoperability with Java, JavaScript, and as of recently C/C++. As well as a concise way of writing code, and an easy way of adjusting to new features you've never used before. Most importantly, creates a relationship between the developer and the compiler. A language that can help developers understand the difference between runtime and compile time programming. The difference between a constant and an immutable value. The use of an operator overload. The use of an extension. The use of class properties. A better way of declaring a singleton. A better way of passing objects around that have non/nullability. A better way of creating classes that aren't made just to store some data (data class), or creating classes that are much like enums but behave like classes (sealed class hierarchy). And so much more. I highly recommend that you start getting into learning kotlin, because of all the above mentioned reasons.

"Now what was that about setting up forge with kotlin? How is that possible? It's a different language. You can't do that, can you?" Yes...yes you definitely can. Forge provides language adapters. Forge has one for scala and java (default), and right now you can use Forgelin for using kotlin for modding. So lemme show you how to do that.

Before continuing you should get Intellij IDEA set up so that you can use kotlin in its native IDE that has a really fast and accurate intellisense and decompiler. You also need to configure your project but I can show you the fast way in a bit. Once you've done that, you can continue.

In your gradle file, assuming that you're in a project, you want to go into the dependencies block. You want to include the following dependencies. Don't mind the commented dependency. I just forgot to remove that, and I'm too lazy to take another screenshot.


You also need gradle to find the dependency through maven, since Forgelin is available on maven. So here's what you need to put where your repositories block is (unless you have stuff there, you can just get the maven url.


You're going to have an error in your gradle if you refresh your gradle project. We need to define the variable kotlin_version. So let's go ahead and specify that.


We also need to include the kotlin gradle plugin for gradle to know how the kotlin code is compiled for this project.



Now in your gradle tab (assuming you have your project opened as a gradle project and you have the toolbar enabled) click the refresh button. When you create a kotlin file for the first time in a project and it's not a kotlin project, you'll be asked to configure your project to include kotlin.

Once you've got kotlin and forgelin included, then we need to set up a simple mod project in kotlin. I'll show you how to make a basic mod class, a block, an item, and how to do an event, cause knowing how to do those three things in kotlin will help you a lot more than you think.

So if you do everything up until the actual making of the main mod class file the same as without kotlin, then we can create a new kotlin file. Just name it whatever you want. It's recommended to do it the same way as you would with a java file. Then you should have just the package declaration in your new kotlin file. We wanna create an object, which is a soft keyword that denotes a singleton class, which is what your main mod class is. Just create it exactly like a class but with a different keyword, but you don't need the curly brackets. Do the normal Mod annoation as well, with your modid, name, version, etc. However, we're going to declare our modid, name, and version above the main mod object as const val's, which tell the compiler that this value is immutable and is known at compile time, aka, a primitive. A val tells the JVM that the value is immutable, or unchanging, during runtime. As long as it has an assigned value, it cannot change at all throughout the program.

This is what we should be looking at now in our IDE's. Now we can attach the Mod annotation to the main mod object. However, we need to include the modLanguageAdapter to the annotation parameters and assign it to "net.shadowfacts.forgelin.KotlinAdapter", which forge will use this path to find the language adapter somewhere in your classpath, which it should be in your extended libraries.

Now we should be looking at this:
This is now runnable. That is a basic mod setup. That is all you need to make a mod in kotlin. But now what about blocks and items and events and all that good stuff? Well, let's try some things!

Now kotlin has functions, which is essentially the same as a method in Java, except functions are more...broad...you know? You can write a function anywhere. Inside an object is called a member function. Outside is called just a function. You can denote a function using the hard keyword fun. You don't need to denote a static or void or int or anything like that. To denote it being static really all depends on how it's being used. To denote a static function, it needs to be in top-level, or basically, not contained within any kind of object like a class or enum or interface. However, if you must, a "static function like in java" isn't really possible. It, again, all depends on how you're going to use it. If you just want an object that has a bunch of values that you can use and whatnot, don't make an object. If you wanna make an object that stores certain kinds of data for different instances, use a data class. If you wanna make a class that has some "static functions or fields" while still keeping the integrity of the class, make said functions/fields either regular top-level functions/vals/vars or make them extensions if you absolutely need them to be part of the class. If all else fails, and you need an object within a class for whatever reason, mark it as a companion.

Then there's also the whole "val and var" thing going on. The difference is mutability. Mutability is the ability to change the value of a variable. Variables in java are by default mutable, unless declared final. In kotlin, you can simplify this by marking it as just a val. Otherwise, use var for all your variables. But I got into the habit of marking it all val by default because a lot of times, maybe most of the time, you're gonna want it to just be a value, not a variable, which the difference is that a value is just a value stored like a variable, and even behaves like one, except it's treated unlike a variable, and rather as just a single value that doesn't change.

Back to the objects thing...objects don't need to be declared like class. They can be declared whenever you need an object. Sometimes, you'd wanna store an anon class that inherits from a class, inside a var/val. You can do that by simply declaring:

The reason for doing this is because sometimes, you don't actually wanna make an object that's just sitting there that's only made into an object just once. Which is why we are going to be declaring all of our blocks and items this way. We only make the items and blocks once so why make a class when it's gonna be constructed once?

The init block you see is the constructor body. If you wanna make it take in a value in the constructor, then that's when you wanna make it as a class, or you can do it the same way but make a superclass that all the objects inherit. Inside the init are some property accesses and reassignments. The way that kotlin works is that all objects have properties. These properties work a bit different than class members do in java. In java you just declare a field like some integer or something and then create a getter for it if it's read only, or a setter along with it if you wanna make it have safe reassignment. However, properties come with setters and getters by default. So when you access a method in a class called like setValue(value), you are doing the same as using a property access as shown in the above example using the unlocalizedName and registryName property. I'm sure you've noticed by now that the registryName is being assigned to a new ResourceLocation object, but no new keyword? Yeah, that's right. Kotlin doesn't have a new. It's a really old keyword and we get the point of it. We know how objects work in programming, and we have since the 80's (90's if you wanna be technical). We can omit the keyword by now. That is all.

I'm sure you've also noticed the : after the object declaration, right? Yeah, that's how you assign a type to something. That's how you assign a type to a var/val, a return type to a function, and a supertype for objects. Easier ain't it? Easier to read, write, and overall just better in general! If you don't agree, then that's okay. It'll grow on you ;P That brings me back to the functions I was talking about earlier. If you wanna give a function a return type, syntax looks like this: fun name(param: Type, another: AnotherType): ReturnType. It applies the the parameters as well. It can get really complicated eventually tho but I might save that for some other tutorial.


I included different ways of declaring different types. They're all primitive. And yes, they are also objects. The reason for this is because they have nullability. Without a ? appended to the end of a type is a nonnull type. At the end, you can see that I did that to a parameter of type Any? and called it "nullable". You can also see that returns are the same.

One thing to note is that semicolons are not at all needed anymore. Parsing algorithms are very well studied and including semicolons to end statements isn't really desirable anymore. There are better ways of ending statements. However, you can use a semicolon for declaring multiple things on a single line. That is all that it is used for.

Now anyways, let's continue with making our mod. Let's go ahead and make an item. Just use the object code that I showed earlier. Make it top-level, and remember, that means that it's not contained anything whatsoever. I like to think of it as a "free-floating function". Now we need another object to be used as our registry handler. This will be annotated just like an event bus subsciber in java, but only with an object. And we wanna make sure that the annotation is passed a parameter of our modid so that it knows what mod it's contained in.

Now inside of this object, we need to create a function that will register our items. We need to create a function with the syntax that I showed you. We also need to make sure that the item object that we created is named appropriately. So let's just name it tutItem. We need to use the RegistryEvent.Register<Item> event as our parameter, just like with every other event. And annotate it with SubscribeEvent. Then call the registry using the property syntax through the event object, and then call the register method and pass in the item object we created.

You can go ahead and try to run it. Go ahead. I dare you...I'll wait.............................................................................okay that's enough. I can't wait any longer!! It's not going to work. That's because forge is looking for a static method. Java has a certain way of compiling static methods. Java has it's own way of creating statics and so does kotlin. A function is only considered static when it is top-level. Forge is looking for an object with a certain annotation. Kotlin lets you annotate files since they get compiled into classes anyways, you shouldn't necessarily do that unless you are using a lot of events. Then you should put all your events into a single file and annotate the file that way. For now, we are going with the non-file approach. If you wanna know more about what we're about to do, go here. If you wanna know about annotating files, then go here.

So let's go ahead and annotate our event subscriber (function) with JvmStatic.

Now if you run it, it should work just as though you did it in java. You're going to get the usual missing json file errors in your console too.

By now I actually forgot to provide a creative tab. So using the property access syntax we went over, set the creative tab in your item constructor/init.

Now you can run your mod. And you should have your new item (without a model) in the creative tab that you created.

Now time to make a block. Let's do the same thing as the item but instead extend Block and pass into the super constructor the material you want as normal. Then set the registry and unlocalized names and the creative tabs and the hardness and everything the same. This time we're going to add an override to our block. We're going to add an override of the Block#getItemDropped and drop the tutorial item.

Kotlin isn't designed in a time when language design was still completely foreign to us. We actually know what's better and what isn't. Kotlin doesn't have the override marker be an optional annotation. It's a required keyword. Now enough of that, let's create the itemblock.

Now we can register the itemblock inside of the item registry. Just change the register method call to ForgeRegistry#registerAll and pass in all your items, including the itemblock. Then create an exact copy of that function and change the name to be about blocks and change the type parameter of the register event to Block. Then register the block like usual.

Now we can run it again. It should work just fine. The jsons work just the same as in java but with kotlin syntax. Now you can go from here and doing even more stuff. Don't hesitate to ask about kotlin. You can ask me on discord if you message. If needed be I can create a discord server. Don't forget to google as much as possible. Kotlin is everywhere right now. Lots of documents, lots of discussions, lots of articles, etc.

I hope this helps out a lot with more than just modding but programming in general! :D

7 comments:

  1. Nice tutorial, I am really liking Kotlin

    ReplyDelete
    Replies
    1. I've had to add this line to make it work
      tutItemBlock.registryName = tutBlock.registryName

      Delete
  2. What's your Discord username and discrim though?

    ReplyDelete
  3. Awesome overview thank you.

    I've been trying to do immutables in java but there's so much boilerplate. Kotlin reminds me of ios swift in a lot of ways, looks great.

    Is inheritance easy with immutables? I noticed it wasn't even possible in Lombok i think.

    ReplyDelete