Skip to content

Instantly share code, notes, and snippets.

@vertexcubed
Created October 16, 2023 15:29
Show Gist options
  • Save vertexcubed/e07096715cf09326ad7d323ec4ef569f to your computer and use it in GitHub Desktop.
Save vertexcubed/e07096715cf09326ad7d323ec4ef569f to your computer and use it in GitHub Desktop.
Pack Dev Scripting for Dummies

Basic Programming Concepts

This gist contains some absolutely basic programming concepts you should understand before starting to write more complicated scripts. It is targeted towards people with absolutely zero programming experience whatsoever. If you're just changing recipes, you can probably copy and paste from a tutorial instead. This is mostly targeted towards modded Minecraft pack development, and as such all examples will be in both JavaScript (KubeJS) and ZenCode (CraftTweaker).

Note: this is written with Minecraft 1.19.2 in mind. Things may be different on older or newer versions!

Basic Syntax

When programming, each line of a file represents a different instruction that your computer runs. These instructions can be various things, from displaying text, to adding numbers, to checking values. In ZenCode, every instruction must end in a semicolon (;). In JavaScript, lines can optionally end in semicolons, but it's not required.

Scripts

Scripts are files that are run when the game launches, or when it is reloaded. In CraftTweaker, all scripts go in <gamedir>/scripts/ and end in .zs. In KubeJS, scripts go in <gamedir>/kubejs/<script type>_scripts/ and end in .js. If you are using CraftTweaker, you must use ZenCode (.zs files), and if you are using KubeJS, you must use JavaSCript (.js files). Make sure you enable file extensions in your file explorer!! You don't want your scripts to end in .zs.txt or .js.txt!

Note: KubeJS has sub folders for different script types. These are client_scripts, server_scripts, and startup_scripts. CraftTweaker does not make this differentiation.

Reloading

When you make changes to a script, your changes aren't applied instantly. They must be reloaded. You can do this with the /reload command in game. In KubeJS specifically, client scripts can be reloaded with the command /kubesjs reload client_scripts (you may need to press F3+T if your changes aren't applied), and startup scripts are reloaded by restarting the game.

Printing and Logs

Often, when testing your code, you'll want to display it in some file for testing. This can be done in a log file. The CraftTweaker log file is found in <gamedir>/logs/crafttweaker.log, and the KubeJS logs are found in <gamedir/logs/kubejs/, with each script type having a different .log file.

We can write to the log file by printing to it. Printing takes in a string (more on that later) and will simply write it to the log file.

ZenCode:

print("Hello World!");
println("This creates a new line at the end!");

JavaScript:

console.log("Hello World!")

Comments

Comments can be added to your script to write text that isn't read as an instruction. This can be used to explain what certain things to. Single line comments are marked with // and multi line comments are marked with /* */.

Example (same in both languages):

// This is a single line comment!
// This is also a single line comment!
/*
This is a multi line comment!
Yippee!
*/

Variables

Variables are a way to store values to be used later. These values can be various things (elaborated in data types section), and can be assigned with an = sign. Variables can optionally be "constants", which cannot be given a new value after being assigned a value.

ZenCode:

var myVariable = 1;
print(myVariable + 2); //this prints 3
val myConstant = "foo"; //this is a constant

JavaScript:

let myVariable = 1
console.log(myVariable + 2) //this prints 3
const myConstant = "foo" //this is a constant

Data Types

All variables have a data type. All information in code is represented by a certain type.

In JS, the main data types you have to worry about are as follows:

  • Number (Represents any number)
  • String (Represents text, notated with single or double quotes around the text)
  • Boolean (Represents a true or false value)
  • Undefined (A variable that has not been assigned a value yet)
  • Null (Represents no value. Different from Undefined!)

In ZC, the main data types you have to worry about are as follows:

  • int (Represents an integer)
  • float (Represents a decimal number)
  • double (Similar to float, but has more precision)
  • bool (Represents a true or false value)
  • string (Represents text, notated with double quotes around the text)
  • char (Represents a single character, notated with single quotes around the character)

Both languages have more primitive data types than what is listed, but these are the main ones you will be using.

In ZC, you can specify the type of a variable by using the as keyword. You cannot do this in JS.

val myNum as int = 2;
val myBool as bool = false;

Basic Math operations

You can perform math on numbers (would be pretty dumb if you couldn't, after all). The basic operators are addition (+), subtraction (-), multiplication (*), division (/), and modulo (%). Modulo returns the remainder of two numbers after dividing.

In addition, there are a handful of shortcut math operations you can perform on variables. ++ will increment a number by 1, -- will decrement a number by 1, += will add some number to a value, -= will subtract some number, *= and /= do the same.

console.log(1 + 2) //3
console.log(3 * 4) //12
console.log(5 % 2) //1, because 5 / 2 = 2 remainder 1
let num = 2
num++ //num = 3
num *= 5 //num = 15
num--; //num = 14

Note: in ZC, dividing ints will not give you a decimal value! So 10/3 = 3, not 3.33333. If you need a decimal value, make one of the values a float or double.

If Statements

If statements allow you to run code only if a certain condition is met. This condition is a boolean value, and will be either true or false. You can also add an "else" block which will run if the condition is false.

ZenCode:

if condition {
    print("Condition is true!");
}
else {
    print("Condition is false!");
}

JavaScript:

if(condition) {
    console.log("Condition is true!")
}
else {
    console.log("Condition is false!")
}

If you want to compare a value, there are various operators you can use. They are == (equals), != (not equals), >, <, >= (greater or equal to), and <=.

Note: JS has two equal to operators, being == and ===. 3 equals signs will also check to make sure the type of the two values is the same!

ZenCode:

if x == 2 {
    print("x is equal to 2!");
}

JavaScript:

if(x == 2) {
    console.log("x is equal to 2!")
}

Logic Operators

There are three main logical operators you can use with boolean values. They are and (&&), or (||), and not (!). And will check if both values are true, or will check if one of the values are true, and not will check if the value is false.

true && true //true
true && false //false
false && false //false
true || true //true
true || false //true
false || false //false
!true // false
!false // true

Arrays

Arrays are a list of multiple values of the same type. They are typically notated with brackets []. In ZC, you must define the type of the array. You can access elements of an array by doing <array name>[<index>]. In ZC, arrays have a fixed length, meaning once initialized you cannot change the length. In JS, you can continuously add more elements to an array. You can get the length by doing <array name>.length.

Important: indexes start at 0, not 1!!!

ZenCode:

val myArray = ["foo", "bar", "baz"] as string[];
print(myArray[0]); //foo
print(myArray[2]); //baz

JavaScript:

const myArray = ["foo", "bar", "baz"]
console.log(myArray[0]) //foo
console.log(myArray[2]) //baz

Loops

Loops allow us to run code multiple times. There are two main types, of loops, being for loops and while loops. For loops run a fixed amount of times, and while loops run while a condition is true.

In ZC, for loops can be defined in multiple ways as shown

for myVal in myArray {
    //defines the variable myVal for each element in array myArray, running the code for each value.
}
for i, myVal in myArray {
    //defines the variables i and myVal. i represents the index of a value in myArray, while myVal represents the value.
}
for i in 0 .. 10 {
    //defines the variable i for each number from 0 to 9 (0, 1, 2, 3, ... 8, 9)
}

In JS, for loops are defined as follows:

for(let i = 0; i < 5; i++) {
    //runs for each number from 0 to 4 (0, 1, 2, 3, 4)
}

The for loop syntax is made of 3 expressions, separated by semicolons.

  • The first expression is run once, when the for loop is first executed.
  • The second expression is a boolean value. When the condition is true, the loop is run.
  • The third expression is run at the end of each loop cycle.

The most standard way of doing this is to define a number to 0, checking if it's less than our desired loop amount, then adding 1 to the value at the end. If you want to loop over values in an array, check if i is less than <array>.length.

While loops are the same in both languages:

while(condition) {
    //Keeps running when condition is true.
}

You can break out of both types of loops using the break statement.

Functions

Functions are bits of shared code that can be called whenever. This makes it easier to reuse code multiple times. All functions can take in arguments, and can optionally return a value. In ZC, you must define the type of each argument, along with the return type (void if it returns nothing).

ZenCode:

function myFunction(arg1 as int) as void {
    print(arg1 + 2);
}
myFunction(2); //prints 4
myFunction(3); //prints 5

JavaScript:

function myFunction(arg1) {
    console.log(arg1 + 2)
}
myFunction(2) //prints 4
myFunction(3) //prints 5

Further reading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment