Update: I created a git repository on GitHub for this series. Check it out! The Repo
Starting from this one, I'll write about making mods in scala. Rather than getting specific jobs done like adding biomes, making skeleton cry, or "how to add a bow that only damage pigs", I'm only going to go as far as making blocks, items and tileEntities (the big three). I'm not a pro in writing all kinds of minecraft stuff, and that kind of tutorial is so sufficient that I'll look like an idiot compared to others. However, I see a need of learning scala because of the ForgeMultiPart library, and a lack of tutorial on scala. I then decided to fill in the blank. I will look into ForgeMultiPart library as I move on, and hopefully can provide some concrete examples in using it .
As a side note, I noticed many people modding have actually very limited knowledge of the tools they are using. They try to get to creating working blocks and items before they even understand what the line new ClassA(arg1, arg2) means, and the differences between int and Integer. I'm not going to lecture about java and OOP, but I do suggest everyone to learn the language and programming concepts before they continue modding. Otherwise, you'll leave yourself a maintenance hell afterwards if you have no idea why you made those mistakes. In that sense, I recommend people who wants to get serious really go invest on an actual programming book. The one I recommend on learning java and OOP is the Head First Java book.
Back to topic. The general impression that scala is hard came from the fact that there is no scala books for absolute beginners. Afterall, functional programming isn't the easiest thing to grasp. However, scala's OOP aspect is no different than java. One can actually copy java code and get rid of public/static, semicolons and return, and it becomes perfectly valid scala code (well not actually, you have to put types after variable names, and use val/var to declare them). One can treat scala as a disguised java in most places. The best advantage of scala is its expressiveness. Even though we have the help of powerful IDEs like eclipse, we still have to type a lot in java when writing loops and declaring classes. Let's admit it, it is BORING! why not just start learning a fun language if you don't know any language before? Forge provides full scala support, so let's utilize that privilege and dive into minecraft with the fun scala!
Tutorial 2 is on setting up basic main mod file with forge. If you don't know how to setup a forge environment, there are tons of tutorial out there, and of course, tutorial 1 written by me. However, I assume you follow the track of my tutorial(installing a gradle environment), and have basic knowledge of how to create new files both by hand and in eclipse.
Steps:
1. We need to modify our build.gradle script to include support for scala. Gradle has built in support, so we just need to add the line:
apply plugin: 'scala'
before the line:
apply plugin: 'forge'
final result:
apply plugin: 'scala'
apply plugin: 'forge'
and that's it. Note that you have to put 'scala' before 'forge', because 'scala' is dependent on 'java', which 'forge' includes implicitly. Thus, the plugin dependency goes in order, where if A depends on B, we put A then B. If you are using eclipse and forge's source distribution, make sure you add the apply plugin: 'scala' line first, and then run "gradlew.bat setupDecompWorkspace", then "gradlew.bat eclipse".
2. Here we are: the main class file. Put it in the src/main/scala folder, and name whatever you want. For best practice, if your package says com.example.examplemod, put the file into src/main/scala/com/example/examplemod folder.
If you are using eclipse, you need to install the scala IDE plugin. Go to Help->eclipse marketplace->search and type in "scala", you'll see it at the top. Choose the latest version and restart eclipse, you'll be able to write scala in eclipse now! Open the forge project as usual, create a folder under src/main called "scala", and create a class as you would in java development. put the following code in.
@Mod(modid = "MineScala", name = "Mine Scala", version = "SNAPSHOT", modLanguage="scala")
object ExampleMod{
val logger = LogManager.getLogger("MineScala")
@Mod.EventHandler
def preInit(e: FMLPreInitializationEvent) {
logger.info("OMG I'm in the preInit method! Hooray!")
}
@Mod.EventHandler
def init(e: FMLInitializationEvent) {
logger.info("OMG I'm in the init method! Hooray!")
}
@Mod.EventHandler
def postInit(e: FMLPostInitializationEvent) {
logger.info("OMG I'm in the postInit method! Hooray!")
}
}
explanations for step 2, where the juices are, but lengthy and technical:
1). same as java, scala organizes code into packages. This is to provide control to scope. You can think of scope as rooms, and the package line as addresses. Everyone in the same room can easily get to each other, but to get to someone in another room, you need that room's address first. This automatically leads to the explanation of import. If B is in room earth.africa.sahara.nowhere, you have to go there every time you want to find B. "import earth.africa.sahara.nowhere.B" acts as a magical door that opens a worm hole to B, and the next time you want to find B, B is easily accessed in place. import keyword brings the name in scope, so you can access it as if it is in the same room from the beginning.
2). @ keyword gives us something called annotations. It's a fancy word for "nametags". By annotate our "TestMod" with fml's "Mod" annotation, we tell fml that this class is our mod class, all the initialization event will be sent here. "modid" is a unique string for identifying your mod. "name" is a human legible name for your own reference. "version" is self explanatory. Notice the modLanguage='scala' part, this is where scala mods differ from java mods. Usually, FML instantializes your class, and put the instance into the @instance field. If you are declaring this class to be an scala object, it is already done by scala, so FML doesn't need to repeat. You just tell FML not to instantialize by adding the line "modLanguage='scala'". The same is true with all the "@Mod.EventHandler" annotations. We just tell fml to call these methods when that event occurs. def keyword defines methods. It is no more than a block of code that does something. In this example, preInit, init, and postInit are all methods that handles the specific event in their parameters. parameters are things to pass to a method. If a method returns nothing as void in java(in scala, they actuall returns (), a type of Unit), it is called a procedure. If the method returns some value, it is called a function. In this case, all three of our methods are procedures. This distinction is very important in functional programming!
3). FML requires that the main mod to include the three event handler methods. You put all your mod related code inside the curly brackets("{" and "}"). According to FML, you want to initialize and register blocks, items, entities and GUIs in the preInit part, do networking registries in the Init part, and do interactions with other mods in the postInit part.
4). scala object and scala class. a scala class is the same as a java's. However, scala doesn't have the static keyword, so they add another type "object", which is a singleton (only has one instance). If you want a place to store your global constants like modid, mod name, you can use an object. In our main mod's case, FML asks mod makers to implement their main mod class as a singleton, and scala object is a natural fit. If you don't know OOP, you can think class as a blueprint, and an instance as the realized object created from that blueprint, like the relationship between architecture blueprint and the actual building.
5). the val keyword. It is used to declare a name as a constant, and assign a value to it. For example, our logger is a constant with a value returned from LogManager.getLogger method. The nature that it is a constant means you cannot assign another value to it later on. so if you say "logger = LogManager.getLogger("SomethingElse")" later, the code will not compile. This is very different from java, where java declares all names as variables by default. If you want to make something constant in java, you need to add the keyword final. Scala can also declare a variable, just replace val with var. However, that is not recommended. Using constants instead of variables is a key practice leading to good functional programming. It is so important that scala has made the val keyword dedicating to declare constants. If you reason thoroughly, you can see there is no need to change the value of logger later on, so a constant is more logical than a variable.
3. Remember that forge's source distribution includes a default mcmod.info file in the "src/main/resources" folder? Let's modify the modid field to whatever you put in your @Mod's modid field. For this tutorial, put in "YourID". This way, forge knows that this mcmod.info file correspond to your mod. Editing of other fields are explained thoroughly in other mods. I'm not gonna reiterate here.
4. This code compiles with "gradle build". After calling "gradle runClient", you can see your mod in the mod menu. If you use eclipse, simply click on run. eclipse will build the project first and run it. Also, check out the log file in your run folder, you should see the lines "OMG I'm in preInit/init/postInit methods! Hooray!" in it.
This tutorial did nothing except adding your mod to the mod menu list, and spam the log file with overly exciting comments. However, the explanation of the syntax is needed for your further understanding of scala. I'm not being thorough here and omitted a lot of details for the sake of simplicity.
Starting from this one, I'll write about making mods in scala. Rather than getting specific jobs done like adding biomes, making skeleton cry, or "how to add a bow that only damage pigs", I'm only going to go as far as making blocks, items and tileEntities (the big three). I'm not a pro in writing all kinds of minecraft stuff, and that kind of tutorial is so sufficient that I'll look like an idiot compared to others. However, I see a need of learning scala because of the ForgeMultiPart library, and a lack of tutorial on scala. I then decided to fill in the blank. I will look into ForgeMultiPart library as I move on, and hopefully can provide some concrete examples in using it .
As a side note, I noticed many people modding have actually very limited knowledge of the tools they are using. They try to get to creating working blocks and items before they even understand what the line new ClassA(arg1, arg2) means, and the differences between int and Integer. I'm not going to lecture about java and OOP, but I do suggest everyone to learn the language and programming concepts before they continue modding. Otherwise, you'll leave yourself a maintenance hell afterwards if you have no idea why you made those mistakes. In that sense, I recommend people who wants to get serious really go invest on an actual programming book. The one I recommend on learning java and OOP is the Head First Java book.
Back to topic. The general impression that scala is hard came from the fact that there is no scala books for absolute beginners. Afterall, functional programming isn't the easiest thing to grasp. However, scala's OOP aspect is no different than java. One can actually copy java code and get rid of public/static, semicolons and return, and it becomes perfectly valid scala code (well not actually, you have to put types after variable names, and use val/var to declare them). One can treat scala as a disguised java in most places. The best advantage of scala is its expressiveness. Even though we have the help of powerful IDEs like eclipse, we still have to type a lot in java when writing loops and declaring classes. Let's admit it, it is BORING! why not just start learning a fun language if you don't know any language before? Forge provides full scala support, so let's utilize that privilege and dive into minecraft with the fun scala!
Tutorial 2 is on setting up basic main mod file with forge. If you don't know how to setup a forge environment, there are tons of tutorial out there, and of course, tutorial 1 written by me. However, I assume you follow the track of my tutorial(installing a gradle environment), and have basic knowledge of how to create new files both by hand and in eclipse.
Steps:
1. We need to modify our build.gradle script to include support for scala. Gradle has built in support, so we just need to add the line:
final result:
and that's it. Note that you have to put 'scala' before 'forge', because 'scala' is dependent on 'java', which 'forge' includes implicitly. Thus, the plugin dependency goes in order, where if A depends on B, we put A then B. If you are using eclipse and forge's source distribution, make sure you add the apply plugin: 'scala' line first, and then run "gradlew.bat setupDecompWorkspace", then "gradlew.bat eclipse".
2. Here we are: the main class file. Put it in the src/main/scala folder, and name whatever you want. For best practice, if your package says com.example.examplemod, put the file into src/main/scala/com/example/examplemod folder.
If you are using eclipse, you need to install the scala IDE plugin. Go to Help->eclipse marketplace->search and type in "scala", you'll see it at the top. Choose the latest version and restart eclipse, you'll be able to write scala in eclipse now! Open the forge project as usual, create a folder under src/main called "scala", and create a class as you would in java development. put the following code in.
Let's see if the formatting works or not:
explanations for step 2, where the juices are, but lengthy and technical:
2). @ keyword gives us something called annotations. It's a fancy word for "nametags". By annotate our "TestMod" with fml's "Mod" annotation, we tell fml that this class is our mod class, all the initialization event will be sent here. "modid" is a unique string for identifying your mod. "name" is a human legible name for your own reference. "version" is self explanatory. Notice the modLanguage='scala' part, this is where scala mods differ from java mods. Usually, FML instantializes your class, and put the instance into the @instance field. If you are declaring this class to be an scala object, it is already done by scala, so FML doesn't need to repeat. You just tell FML not to instantialize by adding the line "modLanguage='scala'". The same is true with all the "@Mod.EventHandler" annotations. We just tell fml to call these methods when that event occurs. def keyword defines methods. It is no more than a block of code that does something. In this example, preInit, init, and postInit are all methods that handles the specific event in their parameters. parameters are things to pass to a method. If a method returns nothing as void in java(in scala, they actuall returns (), a type of Unit), it is called a procedure. If the method returns some value, it is called a function. In this case, all three of our methods are procedures. This distinction is very important in functional programming!
3). FML requires that the main mod to include the three event handler methods. You put all your mod related code inside the curly brackets("{" and "}"). According to FML, you want to initialize and register blocks, items, entities and GUIs in the preInit part, do networking registries in the Init part, and do interactions with other mods in the postInit part.
4). scala object and scala class. a scala class is the same as a java's. However, scala doesn't have the static keyword, so they add another type "object", which is a singleton (only has one instance). If you want a place to store your global constants like modid, mod name, you can use an object. In our main mod's case, FML asks mod makers to implement their main mod class as a singleton, and scala object is a natural fit. If you don't know OOP, you can think class as a blueprint, and an instance as the realized object created from that blueprint, like the relationship between architecture blueprint and the actual building.
5). the val keyword. It is used to declare a name as a constant, and assign a value to it. For example, our logger is a constant with a value returned from LogManager.getLogger method. The nature that it is a constant means you cannot assign another value to it later on. so if you say "logger = LogManager.getLogger("SomethingElse")" later, the code will not compile. This is very different from java, where java declares all names as variables by default. If you want to make something constant in java, you need to add the keyword final. Scala can also declare a variable, just replace val with var. However, that is not recommended. Using constants instead of variables is a key practice leading to good functional programming. It is so important that scala has made the val keyword dedicating to declare constants. If you reason thoroughly, you can see there is no need to change the value of logger later on, so a constant is more logical than a variable.
3. Remember that forge's source distribution includes a default mcmod.info file in the "src/main/resources" folder? Let's modify the modid field to whatever you put in your @Mod's modid field. For this tutorial, put in "YourID". This way, forge knows that this mcmod.info file correspond to your mod. Editing of other fields are explained thoroughly in other mods. I'm not gonna reiterate here.
4. This code compiles with "gradle build". After calling "gradle runClient", you can see your mod in the mod menu. If you use eclipse, simply click on run. eclipse will build the project first and run it. Also, check out the log file in your run folder, you should see the lines "OMG I'm in preInit/init/postInit methods! Hooray!" in it.
This tutorial did nothing except adding your mod to the mod menu list, and spam the log file with overly exciting comments. However, the explanation of the syntax is needed for your further understanding of scala. I'm not being thorough here and omitted a lot of details for the sake of simplicity.