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.
"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.
Now we should be looking at this:
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 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.
here. If you wanna know about annotating files, then go here.
So let's go ahead and annotate our event subscriber (function) with JvmStatic.
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.
I hope this helps out a lot with more than just modding but programming in general! :D