JS Scripting - ForgeEssentials/ForgeEssentials GitHub Wiki
Starting with FE 1.4.5 (for MC 1.7.10), FE 1.8.6 (for MC 1.8.9) and the new builds for MC 1.10.2, ForgeEssentials integrates a completely new and powerful scripting system.
This system is based on the new Nashorn JavaScript engine which is bundled starting with Java 8, providing rich features and high performance.
Whilst it is possible to write your custom scripts as plain JavaScript, ForgeEssentials also automatically generates type definitions for all available features. So, not only can you write your scripts in oldschool JavaScript, but you can also use TypeScript, which we strongly recommend for it's awesome features like API documentation, code completion and error detection.
When using TypeScript to write your script, if you are doing something wrong, your editor will tell you. Also, TypeScript provides auto-completion for methods and fields, which makes writing scripts a lot easier.
However, you will need to transpile your TypeScript files back to Javascript, since ForgeEssentials cannot transpile Typescript natively.
Alternatively, you can run /script upgrade
to convert a pattern script (per legacy scripting documentation) into a Javascript script.
You will need an editor to make scripts. You can use any editor of your choice, but if you don't have one, we recommend Visual Studio Code.
- Download and install Visual Studio Code
- In VSCode, go to File -> Open Folder, and open the directory
./ForgeEssentials/JScripting
You should expect to see a screen like so:
ForgeEssentials should have already created the necessary tsconfig.json
, mc.d.ts
and fe.d.ts
inside the JScripting directory to help you start coding right out of the box. If this is not the case, you need to re-check your ForgeEssentials installation. The most common cause is not running the server with the mod installed at least once, so try that first.
Lets start coding!
We will write a script that makes a custom command, for players to check how many hours they have played on your server.
This example will presume you are using VSCode.
Make a new script by right-clicking in the explorer pane, and choosing New File
We will call this script yourscript.ts
.
Firstly, lets make a method to contain our logic, and have it output plain text when called.
function playTime(args: fe.CommandArgs){
Server.chat("Player has played for X hours.")
}
It would be better if we had the player's name in there. We can do that by taking it from the command arguments passed into the method. args.player
contains this information.
function playTime(args: fe.CommandArgs){
const playerName = args.player.getName();
Server.chat(playerName + " has played for X hours.")
}
Next, we will need to get the player's total playtime, as stored by ForgeEssentials, in seconds. This one is a bit more tricky. FEServer.getTimePlayed
returns a number of seconds only when provided with a player's UUID, so we will need this first.
function playTime(args: fe.CommandArgs){
const playerName = args.player.getName();
const playerUuid = args.player.getUuid();
const secondsPlayed = FEServer.getTimePlayed(playerUuid)
Server.chat(playerName + " has played for X hours.")
}
Lastly, divide this seconds count by 3600, and then round down, to extract the number of full hours played. Include this value in the server chat output.
function playTime(args: fe.CommandArgs){
const playerName = args.player.getName();
const playerUuid = args.player.getUuid();
const secondsPlayed = FEServer.getTimePlayed(playerUuid)
const hoursPlayed = Math.floor(secondsPlayed / 3600);
Server.chat(playerName + " has played for " + hoursPlayed + " hours.")
}
For this function to be callable in-game as a command, we must register it as a command with ForgeEssentials.
FEServer.registerCommand({
name: "playtime",
usage: "/playtime",
permission: 'fe.commands.playtime',
opOnly: false,
processCommand: playTime,
tabComplete: playTime,
})
Note that for the permission option, you can set any custom permission node you would like to use here. It's helpful to name it after the command's usage name, but it's not a requirement.
The full script should look like this:
function playTime(args: fe.CommandArgs){
const playerName = args.player.getName();
const playerUuid = args.player.getUuid();
const secondsPlayed = FEServer.getTimePlayed(playerUuid)
const hoursPlayed = Math.floor(secondsPlayed / 3600);
Server.chat(playerName + " has played for " + hoursPlayed + " hours.")
}
FEServer.registerCommand({
name: "playtime",
usage: "/playtime",
permission: 'fe.commands.playtime',
opOnly: false,
processCommand: playTime,
tabComplete: playTime,
})
If you are using TypeScript as recommended, then for your completed script to function in-game, it must be transpiled from TypeScript to JavaScript.
The process described below assumes you have access to an installed terminal of your choice (bash, powershell, etc).
You will want to start by checking you have access to Node.js and npm. You can check by running node --version
and npm --version
. If you do not have these, you can install through the instructions at the npm Docs here.
- In VSCode, go to Terminal -> New Terminal. A default terminal should appear at the bottom of the editor.
- Click the "Launch Profile" button, as pictured below, to choose your preferred terminal (if you have one)
You now have access to your terminal within the VSCode editor. The current directory of the terminal should already be at your ./ForgeEssentials/JScripting
. If it is not, make sure to navigate there first.
...you can either open a terminal within your editor of choice, or open your terminal separately, and then navigate to the ./ForgeEssentials/JScripting
where yourscript.ts
is located.
- Run
npm install -g typescript
.
What this command does, is installs TypeScript "globally" to be used anywhere on your machine, i.e installed in the shared node_modules
folder as part of your Node installation. You can reverse this installation by running npm uninstall -g typescript
at any time. Installing the TypeScript npm package gives you access to the tsc
TypeScript compiler, more details can be read about this here
- Run
tsc yourscript.ts
Newer versions of tsc ignore the tsconfig file if you specify a ts file it seems. To work around, either run tsc at the project level, (no arguments), or instruct tsc to only use the es5 lib. ex: tsc -lib es5 mc.d.ts fe.d.ts yourscript.ts
This will now transpile yourscript.ts
and write the output to the JScripting
folder as yourscript.js
And that's it! Fire up your server, and try out your new command!
The API is documented as TypeScript typings, which are automatically generated from the internal wrapper classes used to access Minecraft / Forge / ForgeEssentials features.
Type definition files (mc.d.ts
and fe.d.ts
) are attached to the release and should be included when writing scripts with a compatible TypeScript editor.
The following example shows the location of all players in the current dimension:
function radarCommandHandler(args) {
if (!args.isEmpty()) {
return args.confirm('This command has no arguments');
}
args.player.getWorld().getPlayerEntities().forEach(function (player) {
args.confirm('Player ' + player.getName() + ' is at [' +
player.getX() + ',' +
player.getY() + ',' +
player.getZ() + ']');
});
}
FEServer.registerCommand({
name: 'radar',
usage: '/radar: Show all player in this world',
permission: 'commands.radar',
opOnly: false,
processCommand: radarCommandHandler,
tabComplete: radarCommandHandler,
});
And here is the same script, written with TypeScript typings:
function radarCommandHandler(args: fe.CommandArgs) {
if (!args.isEmpty()) {
return args.confirm('This command has no arguments');
}
args.player.getWorld().getPlayerEntities().forEach((player) => {
args.confirm(`Player ${player.getName()} is at [${player.getX()},${player.getY()},${player.getZ()}]`);
});
}
FEServer.registerCommand({
name: 'radar',
usage: '/radar: Show all player in this world',
permission: 'commands.radar',
opOnly: false,
processCommand: radarCommandHandler,
tabComplete: radarCommandHandler,
});
More examples like this are available from the project repository.