Introduction
ChatTriggers is a framework for Minecraft Forge that allows for mods to be scripted, in languages such as JavaScript. Scripts are able to be hot reloaded, which means you can make changes to your mod without restarting!
JavaScript scripts are executed with our custom fork of Mozilla's Rhino library. Our custom fork supports many ES6 and ESNEXT features. For the (almost) full feature list,check our compatibility table;
Setup
To set up ChatTriggers, all you have to do is put the ChatTriggers jar into your mods
folder,
and launch Minecraft. By default, ChatTriggers modules are stored in
.minecraft/config/ChatTriggers/modules/
, but this can be changed in the
configuration. To access the ChatTriggers settings, type /ct settings
in game. The
rest of this tutorial will refer to this directory as the "modules directory",
and will assume it is in the default location.
Creating a module
To create a module, create a folder in your modules folder. The folder can have
whatever name you want, but typically it is just the name of the module. Our
module will be called ExampleModule. Our folder structure now looks like
.minecraft/config/ChatTriggers/modules/ExampleModule
.
The metadata file
An example
metadata.json
file
{
"name": "ExampleModule",
"creator": "YourNameHere",
"description": "Our first module!",
"version": "1.0.0",
"entry": "index.js"
}
All modules MUST have a metadata.json
file.
This file contains important information about our module. You can see an
example of this file to the right. The metadata file contains a number of
important fields, documented here:
name
: The name of the modulecreator
: The name of the module creatorversion
: The version of the module. This should conform to SEMVER and should be the same as the version selected when uploading if you wish to upload it to the website.entry
: This is the name of a file that should actually be ran. This key is necessary if, for example, your module registers triggers or commands. This field is REQUIRED for all modules that wish to run code, without it your module will NOT run. If all your module provides is a library that other modules can use, this is not needed.requires
: An array of names of other modules that your module depends on. These modules are guaranteed to be loaded before your module, allowing you to use them directly.asmEntry
andasmExposedFunctions
: The ChatTriggers wiki goes into detail about ASM here
Scripting
We now need to create our script files. Typically, the root file of your module
is named index.js
. This is a general web development practice. You can name
your files whatever your want, however one benefit of having an index.js
file
is that if someone tries to import from your module folder directly, instead of
a specific file in your module, it will be imported from the index.js
file, if
one exists. If no index.js
file exists, other people will have to import
directly from the files themselves.
The Basics
Registering a Trigger
We register a WorldLoad trigger like so:
The argument passed into the register function is the function you want to trigger:
function exampleWorldLoad() {
}
register("worldLoad", exampleWorldLoad);
You can also register an anonymous function for simplicity:
register("worldLoad", () => {
});
In ChatTriggers, "triggers" are events that get fired
when a certain action happens in game, like a sound being played or a chat
message being sent. Let's start with one of the simplest triggers: WorldLoad. In
order to register a trigger, we use the provided register
function. It takes
the trigger name as the first argument (case-insensitive), and the function to
run as the second argument. You can see a complete list of these triggers on our
javadocs, under IRegister
.
Now any code inside of our exampleWorldLoad
trigger will be ran whenever a world is loaded. From here, you
can do many things, such as interacting with Minecraft methods or getting information from arguments that
certain triggers may pass in.
The "Chat" Trigger
A basic chat trigger
register("chat", (player, message, event) => {
ChatLib.chat(player + ": " + message);
}).setCriteria("<${player}> ${message}");
This is how you can set a chat trigger. Chat triggers trigger when a chat message matches the specific criteria that is set.
In this case, the trigger will fire whenever a chat message matches a chat message <${player}> ${message}
. You can reference
variables by adding ${ }
around the variable name. All variables must be accounted for in the function parameters, following the order they appear in.
Also keep in mind that the event that is fired is always the last parameter.
You may notice that the original message is still being sent, which looks really ugly. To fix this, we can cancel the event.
register("chat", (player, message, event) => {
cancel(event);
ChatLib.chat(player + ": " + message);
}).setCriteria("<${player}> ${message}");
Setting criteria as it is in the example to the right will try to match the exact message. In order to allow a message to just
contain the criteria, you can add .setContains()
after setCriteria
. An example of this is to the right. With this, hi Player! how are you?
, etc. will also trigger. These are just simple examples, but the idea still is there. The message just has to contain the criteria you set when you add .setContains()
. There are also setStart
and setEnd
modifiers you can use instead.
Also, if the criteria you set contains color codes (starting with § or &), the message will try to match the exact color throughout the message.
This will be triggered if a player says
hi User!
anywhere inside their message
register("chat", (player, event) => {
ChatLib.chat("howdy " + player);
}).setCriteria("hi ${player}!").setContains();
When trying to pick up certain messages that aren't sent by other players, try not to use setContains
as a player could type the message that's being matched and trigger your code.
MessageSent Trigger
Display "Pong!" in response to YOUR message sent containing "ping"
register("messageSent", (message, event) => {
if (message.toLowerCase().includes("ping")) {
ChatLib.chat("Pong!");
}
});
In addition to letting you know when an event has occurred by calling your function, many triggers pass through additional information about their respective event. Let's take a look at a different trigger: MessageSent. This trigger is fired whenever the client (you) sends a message.
Let's make a trigger that, whenever you sent a message with the word "ping", displays the message "Pong!". In order to do this, we need to accept the arguments passed in by the MessageSent trigger. You can see all the arguments that a trigger passes through in the javadocs linked above. The MessageSent trigger passes in the message event and the actual message.
Many triggers are cancellable, meaning that they actually fire before the event in question happens. In our case, our MessageSent trigger will fire before the chat message is actually sent. Cancelling the event is as easy as calling cancel(event)
, however we won't do that here.
We are interested in the message parameter. We simply check if it contains the word we are interested in, and if so, we use the ChatLib
utility to send a message only visible to the player. You can read more about the ChatLib
utility here
Command Trigger
Another one of the most common triggers is the Command trigger. This allows the user to make custom commands of their choosing.
register("command", (user) => {
ChatLib.chat("Hi " + user);
}).setName("mycommand");
Commands are one of the few triggers that do not have event as a parameter. This example creates a command which can be called through /mycommand
. The arguments of the function are the arguments the user types into the command. If the user types /mycommand kerbybit
, then the function will be triggered, and Hi kerbybit
will be printed into chat.
We can also set command aliases using the setAliases
function. It accepts any amount of arguments. Let's say we want /mc
and /myc
to also be valid commands that will trigger our code. We would just add it anto the trigger, in no specific order.
register("command", (user) => {
ChatLib.chat("Hi " + user);
}).setName("mycommand").setAliases("mc", "myc");
Module Organization
ES6 style import syntax (preferred)
import playerLocation from "WhereAmi";
This imports a function from WhereAmI's index file. You could specify additional files i.e. WhereAmI/otherfile
You can also import from other files within the current module using local file paths such as ./folder/file
(where folder resides within WhereAmI).
This imports a function from another module, called WhereAmI, to get a players location.
When making a module, it is important to know how ChatTriggers loads your files so you can organize them efficiently. When your module is loaded, only the file specified as the entry in metadata.json
is loaded. Any other code you want to run must be imported, through the ES6 style import
syntax.
ChatLib
The ChatLib is a utility provided to imports for interacting with Minecraft chat. It has functionality to send, edit, and delete messages in Minecraft's chat. You can create clickable, and hoverable, text in chat, run commands, and much more.
Sending messages
This sends a client-side message in chat
ChatLib.chat("Coming from the code!");
This first example shows how to display a basic chat message. This message differs from normal messages in that it does NOT trigger chat triggers. It is also not visible to other players.
Message objects
This example sends messages that have clickable and hoverable text
const clickableMessage = new Message(
"This is not clickable. ",
new TextComponent("This is clickable").setClick("run_command", "/help"),
"!"
);
const hoverableMessage = new TextComponent("This message does nothing when clicked.").setHoverValue("But it shows this text when hovered over!");
ChatLib.chat(clickableMessage);
ChatLib.chat(hoverableMessage);
Here we are creating new Message objects. These are required to send messages that have clickable or hoverable text.
The constructor of a Message can take as many String
s or TextComponent
s as you want, simply separate them with commas as shown in the first example.
TextComponents are nice little wrappers that allow you to customize a small chunk of a message. You can change what happens when you click or hover on the message. You can also directly send a TextComponent as seen with the hoverable message. All of these can use formatting codes with the &
symbol.
Clickables
The first message we create is a message that has clickable, and non-clickable, text. The first part is regular text,
followed by a clickable part that runs the /help
command when clicked, and shows the hoverText when the mouse is
hovering over that part of the message. Then, at the end, it has a non-clickable exclamation point.
Hovers
The second message created and chatted is a message that only contains a hoverable message. Nothing will be activated or ran when the message is clicked.
Message IDs
This is how you send a chat message with an ID, and then delete it
new Message("This will be deleted!").setChatLineId(5050).chat();
ChatLib.clearChat(5050);
Every message can have an ID specified, which can then be deleted from chat. This is best suited for auto-replacing menus, chat messages you only want to display in chat for a certain amount of time, etc.
This example also showcases an alternative method of chatting a Message, as there is a simple helper method, Message#chat()
Only one chat message can have the same ID, as it will replace any messages with the same ID before sending.
The ID is specified in the message object, and you pass the same ID to ChatLib.clearChat(id)
to delete it.
Editing chat
This is how you edit a chat message after it has been sent to chat
ChatLib.chat("Hey there! This will change...");
ChatLib.editChat("Hey there! This will change...", "And... changed!")
ChatLib.editChat(message, replacer)
is a simple method that takes in an unformatted message and replaces all instances of it with the replacer. This is a slightly laggy operation if done extremely rapidly (i.e. around 60 times per second). The editChat
method can also take the Message ID as the first argument.
Specially formatted messages
This is how you center a chat message
ChatLib.chat(ChatLib.getCenteredText("This is in the center of the chat!"));
This is how you make a line break
ChatLib.chat(ChatLib.getChatBreak("-"));
Centered messages
To center a message in chat (padded by spaces), use the ChatLib.getCenteredText(text)
method, and then chat what's
returned.
Line breaks
To create a line break in chat that will be the exact length of the chat box no matter the user's width and size
settings, use the ChatLib.getChatBreak(seperator)
method. This can take any seperator, like "-" as we used in the
example.
Chat message from event
This is how you get the unformatted message from a chat event
function onChatReceived(event) {
const unformattedMessage = ChatLib.getChatMessage(event);
}
This is how you get the formatted message from a chat event
function onChatReceived(event) {
const formattedMessage = ChatLib.getChatMessage(event, true);
}
Unformatted chat
To get the unformatted chat from a chat event passed into a function by a Chat Trigger, pass it to the
ChatLib.getChatMessage(event)
method, which returns an unformatted string.
Formatted chat
However, if you want the formatted version of the chat message, append the true
flag to the
ChatLib.getChatMessage(event, formatted
) method.
Rendering
Rendering is where modules can draw most anything on to the game screen. All 2D rendering involves
calling methods in the Renderer
object. 3D rendering involves calling methods in the
Tessellator
object.
Setting up
Function to be ran everytime the game overlay is rendered
register("renderOverlay", myRenderOverlay);
function myRenderOverlay() {
}
Rendering has to be done every frame of the game, otherwise it will only be on the screen for one frame. There are many different render triggers, and they all start with Render
. The most common render trigger is RenderOverlay: this trigger fires every frame with no conditions.
Setting priority
It is possible to set a certain trigger's priority like so
register("renderOverlay", myRenderOverlayLast).setPriority(Priority.LOWEST);
register("renderOverlay", myRenderOverlayFirst).setPriority(Priority.HIGHEST);
function myRenderOverlayLast() {
}
function myRenderOverlayFirst() {
}
Here, were are dealing with the priority of triggers. Priorities are LOWEST, LOW, NORMAL, HIGH, HIGHEST
.
Triggers with a priority of HIGHEST are ran first, because they have first say on an event. Triggers with a priority of LOWEST are ran last. The function lan rast will draw on TOP of anything before it.
Simple text rendering
You can render text onto the screen with this code
register("renderOverlay", myRenderOverlay);
function myRenderOverlay() {
Renderer.drawString("Hello World!", 10, 10);
}
Every frame, the code inside myRenderOverlay
is called. Inside of this function, we make one call
to Renderer.drawString(text, screenX, screenY)
. We make the text say "Hello World!", and place it on the screen
at 10, 10 (the top left corner).
More complex text rendering
This is how you would draw the same string (but colored) with an object
register("renderOverlay", myRenderOverlay);
const myTextObject = new Text("Hello World!", 10, 10).setColor(Renderer.RED);
function myRenderOverlay() {
myTextObject.draw();
}
Here, instead of simply making a method call, we are instatiating an object to do our drawing. This allows for much greater customization, such as rotation, scaling, and as described below, coloring.
The other interesting part to take a look at is the call to setColor
, which will, as you can guess, set the color of the text. For the color, we use a preset color in Renderer
. We could have also made a call to Renderer.color(red, green, blue, alpha)
, and subsequently passed that in to the method. In this example, that call would be Renderer.color(255, 255, 255, 255)
. Values should range from 0-255.
Rendering of shapes
This example renders a rectangle, circle, and triangle
register("renderOverlay", myRenderOverlay);
const rectangle = new Rectangle(Renderer.WHITE, 10, 10, 50, 50);
const circle = new Shape(Renderer.WHITE).setCircle(100, 100, 25, 360);
const polygon = new Shape(Renderer.WHITE)
.addVertex(300, 300)
.addVertex(400, 400)
.addVertex(200, 400);
function myRenderOverlay() {
rectangle.draw();
circle.draw();
polygon.draw();
}
Let's look at more complex rendering using shapes. We can see our first complex piece of rendering code to the right. The first thing to notice is how we define the shapes outside of our render trigger. This way we aren't create three objects 60+ times a second.
The different shape classes
We create the first shape, the rectangle, with the instantiation of the Rectangle
class, whose constructor takes the following arguments: color, x, y, width, and height.
The next shape is a circle, which we create through the more general Shape
class, which is just a collection of (x, y) points to connect together. We use the setCircle
method, which automatically populates the Shape's vertices to give us a perfect circle.
Finally, we manually configure the vertices of the last shape ourselves with the addVertex
method. After we have created all of our shapes outside of the trigger function, we call the draw
method inside of the trigger function to render them to the screen.
Rendering images
This example renders the images on the screen
register("renderOverlay", myRenderImageOverlay);
const image = new Image("ctjs-logo.png", "http://ct.kerbybit.com/ct.js/images/logo.png");
function myRenderImageOverlay() {
image.draw(100, 100);
}
As before, we register for a render overlay trigger so we can do our rendering in it. However, this time, we create an Image
object with the file name and URL. The file will download to the ChatTriggers assets directory, which defaults to .minecraft/config/ChatTriggers/images
.
Advanced rendering
Here we are rendering text that is a rainbow color
register("renderOverlay", myRenderOverlay);
const text = new Text("Rainbows!", 10, 10);
let exampleImportStep = 0;
function myRenderOverlay() {
text.setColor(Renderer.getRainbow(exampleImportStep));
text.draw();
exampleImportStep++;
}
This topic covers advanced rendering, like rainbow colors and dynamic positioning.
Rainbow colors
Again, we setup the default rendering scheme of a RenderOverlay trigger and its corresponding function. However, this time we also create a "exampleImportStep" variable that starts of at 0. Then, every time we render to the screen, we increment this step variable by 1.
This variable is used when it is passed into the Renderer.getRainbow(step)
method, which produces rotating colors,
which we then use as the color for the drawString method.
Dynamic positioning
This example showcases how to make render positioning dynamic
register("renderOverlay", myRenderOverlay);
const width = Renderer.screen.getWidth();
const rectWidth = 50;
const textStr = "White Text!";
const rectangle = new Rectangle(Renderer.WHITE, width / 2 - rectWidth / 2, 200, rectWidth, 50);
const text = new Text(textStr, width / 2 - Renderer.getStringWidth(textStr) / 2, 100).setColor(Renderer.WHITE);
function myRenderOverlay() {
text.draw();
rectangle.draw();
}
Here we are making all of our rendered objects be perfectly aligned horizontally on the screen for all windows sizes. We start off by getting the height of the current window, with the call to Renderer.screen.getWidth()
.
Then, for each part we render, we get half the width of the window, and then subtract half the width of our rendered object. For a string, this is done with (width / 2) - (Renderer.getStringWidth(textToRender) / 2)
. For a fixed width object, you can replace Renderer.getStringWidth(textToRender)
with the width of the object. Notice how we use the Text
object and instantiate it outside of the render function. The Text
object allows you to set additional properties, such as shadow and color.
Tessellator
The Tessellator object is meant for rendering in 3 dimensions. You have to call this object under the renderWorld
trigger.
Drawing Text
You can set floating text at specific world coordinates using
Tessellator.drawString(text, x, y, z)
register("renderWorld", myWorldRender);
function myWorldRender() {
Tessellator.drawString("Hello there!", 100, 100, 100);
}
This will draw the words "Hello There" at the world location (100, 100, 100)
.
You can also change the text color and scale, whether or not to render the shadow, and whether or not
the size increases from distance.
Drawing Textures
You can also draw specific textures like so
register("renderWorld", myWorldRender);
const img = new Image("kerbybit.png", "https://www.chattriggers.com/assets/images/kerbybit.png");
function myWorldRender() {
Tessellator.bindTexture(img);
Tessellator.begin()
.translate(Player.getX(), Player.getY(), Player.getZ())
.pos(-0.5, 0.5, -0.5).tex(0, 0)
.pos(-0.5, 0.5, 0.5).tex(0, 1)
.pos(0.5, 0.5, 0.5).tex(1, 1)
.pos(0.5, 0.5, -0.5).tex(1, 0)
.draw();
}
This will draw kerbybit's skin texture at the player's feet position. This works by first binding the texture
of the image to the Tessellator
object. It then associates positions in the 3D world (relative to the
Tesselator
's current position) to points on the texture. The tex
function takes an x
and
y
argument, both ranging from 0.0
to 1.0
, which identifies a point on the texture (i.e.
.tex(0.3, 0.7)
refers to the point 30% across from the left and 70% down from the top).
Finally, a call to .draw()
finished the operation.
Partial Ticks
Partial ticks are a fractional value representing the amount of time that’s passed between the last full tick and now. This is useful in rendering, as the rest of the game runs on the tick system, but rendering uses frames, a much more precise operation. We use this because otherwise the animation would be jittery because there are fewer ticks per second than frames per second.
If we wanted to make the example from above not be jittery, we would use partial ticks.
const myPlayerMP = new PlayerMP(Player.getPlayer());
function myWorldRender(partialTicks) {
const lastX = myPlayerMP.getLastX();
const lastY = myPlayerMP.getLastY();
const lastZ = myPlayerMP.getLastZ();
const currentX = Player.getX();
const currentY = Player.getY();
const currentZ = Player.getZ();
Tessellator.bindTexture(img);
Tessellator.begin()
.translate(
lastX + (currentX - lastX) * partialTicks,
lastY + (currentY - lastY) * partialTicks,
lastZ + (currentZ - lastZ) * partialTicks
)
.pos(-0.5, 0.5, -0.5).tex(0, 0)
.pos(-0.5, 0.5, 0.5).tex(0, 1)
.pos(0.5, 0.5, 0.5).tex(1, 1)
.pos(0.5, 0.5, -0.5).tex(1, 0)
.draw();
}
The key difference here is that the change in position is multiplied by the change in time every frame. This makes the drawing smooth since it updates every frame instead of every tick.
Objects
ChatTriggers provides several objects to expand the functionality of your imports without you needing to delve into base Minecraft code. A list is found below.
Object | Description |
---|---|
Book | Makes an openable book in Minecraft |
CPS | Contains information about the player's clicks per second |
Display | Renders text on to the game screen |
Gui | Makes an openable gui in Minecraft |
Inventory | Contains information about the player's inventory |
KeyBind | Used for detecting a key's state |
ParticleEffect | Allows creation of custom particle effects to be displayed client side |
Player | Used for getting information about the player |
Thread | This is a pseudo object, used to do tasks that take a long time |
Books
Book
objects are used for displaying base Minecraft book GUIs with customizable text.
Creation
This is how you create a book
const book = new Book("Example Book");
We create our book with the Book constructor of new Book(bookName);
. We want to create our book in the global scope,
as explained below.
Adding content
This is how you add pages to the book
const book = new Book("Example Book");
book.addPage("This is a very simple page with just text.");
book.addPage(new Message("This is a page with a ", new TextComponent("twist!").setHoverValue("Hi! I'm hover text :o")));
To add content to our book, we'll want to utilize the .addPage(message)
method. This can take either a simple
string as the message for the page, or a Message
object if you want to utilize the functionality the provide, covered
here. This should be done right after the instantiation of the book.
Updating content
This is how to update a page's content
book.setPage(1, new Message("lol!"));
To set a page, we use the .setPage(pageNumber, message)
. Page number is the number of the page you wish to update,
0 based. The message has to be a Message
object, there is no method for just a string.
This can be done anytime, just re-display the book to see the updated version. The page you try to set must already exist, or else there will be errors. Just add the page if you need to add a new page afterwards.
Displaying
This is how to display the book to the user
book.display();
This is how to display the book starting on a certain page
book.display(1);
This is a very simple operation which just opens the book. You can also specify a page number to open the book to as the first argument, it defaults to 0. If the page you specify doesn't exist, the player will have to click one of the arrows to go to the next available page.
CPS
The
CPS
object gives information about the player's clicks per second.
Clicks per second
To get the left clicks per second, use this
const leftClicks = CPS.getLeftClicks();
To get the right clicks per second, use this
const rightClicks = CPS.getRightClicks();
There are more methods for the CPS
object, but these are the most common. You can always see a full list
of up to date documentation on the JavaDocs.
Displays
Displays are used for rendering simple text on to the players screen. If you would like to utilize other rendering functions found in the rendering section, use custom rendering functions.
Creation
This is how you can create a
Display
object
const display = new Display();
This Display
object is now created, but it doesn't do much of anything yet.
Adding content
This is how you can add lines to a display
display.addLine("Ay! First line.");
display.addLines("2nd line", "3rd line");
display.addLines(2);
display.addLine(new DisplayLine("2 line gap above me!"));
Displays
consist of lines of text. These lines can be added and set, and they can use color codes. The first call to
.addLine(message)
adds a line to the display with the text passed in. The second call to .addLines(messages...)
adds
as many lines as you pass into it, in our case just 2. If you don't pass in Strings or DisplayLines
, then it will just add one
line per parameter you passed (e.g .addLines(2)
will only add 1 empty line despite the number, but .addLines(0, 0)
will add 2 empty lines due to there being 2 parameters).
Setting content
This is how you set a line in a display
display.setLine(3, "Now this line has text :)");
In this example the call to .setLine(lineNumber, message)
sets the 4th line in the display (0 based) which was previously
blank to our example text. This is what we would use if we want to update a display with information, like the player's
current coordinates.
Setting positioning
This is how you set the alignment of a display
display.setAlign("left");
This aligns your display on the left side of the screen. Other options are "center"
and "right"
.
This is how you set the order of the lines. Both of these ways will work, setting the order to go down.
display.setOrder("down");
display.setOrder(DisplayHandler.Order.DOWN);
This renders the lines from 0 going downwards, usually what you'd want. The other option is "up"
.
This is how you set the exact position of the display
display.setRenderLoc(10, 10);
This sets the X and Y coordinate of where your display should start, with the first argument being X, and the second being Y.
Setting background and foreground options
This sets the background color of the display
display.setBackgroundColor(Renderer.AQUA);
This makes the background color of the display aqua. Other options are all the colors in Renderer, or a custom color
with Renderer.color(r, g, b, a)
.
This sets the type of background for the display to be fit per line. Both of these options will work.
display.setBackground(DisplayHandler.Background.PER_LINE);
display.setBackground("per line");
This option sets how the background color should be displayed. PER_LINE
says that the background should be the width
of each line. NONE
would mean don't show background color, and FULL
would indicate make the background color draw in
a box around the entire display.
This sets the foreground (text) color of the display
display.setTextColor(Renderer.BLUE);
All text in the display will now show blue. This method can take any Renderer color, including custom ones described above.
DisplayLine can also be used to set the text color, background color, and alignment for a specific line.
display.addLine(
new DisplayLine("This will have green text!").setTextColor(Renderer.GREEN)
);
This will set the new line to have green text, overriding the blue we set earlier.
Guis
Guis are screens that are opened in game, such as the chat gui, or the escape menu. These stop the player from moving.
Creation
This is how you create a gui
const gui = new Gui();
Like other objects, creating a Gui is very simple.
Rendering the gui
This is how you set up a function to render the gui
gui.registerDraw(myGuiRenderFunction);
function myGuiRenderFunction(mouseX, mouseY, partialTicks) {
Renderer.drawRect(Renderer.WHITE, mouseX, mouseY, 50, 50);
}
Everything inside of the "myGuiRenderFunction" will be ran while the gui is open. Inside of this function you should make use of the Renderer functions to draw what you want on to the screen. The three arguments passed in are the x coordinate of the user's mouse, the y coordinate of the users mouse, and the partial ticks.
In this example, we render a 50x50 square with the top left corner being the user's mouse position.
Adding interactivity
This is how you detect when a user presses a key
gui.registerKeyTyped(myGuiKeyTypedFunction);
function myGuiKeyTypedFunction(typedChar, keyCode) {
ChatLib.chat("You typed " + typedChar);
}
This is how you detect when the user clicks
gui.registerClicked(myGuiClickedFunction);
gui.registerDraw(myGuiRenderFunction);
let renderSquareX = 0;
let renderSquareY = 0;
function myGuiRenderFunction(mouseX, mouseY, partialTicks) {
Renderer.drawRect(Renderer.WHITE, renderSquareX, renderSquareY, 50, 50);
}
function myGuiClickedFunction(mouseX, mouseY, button) {
renderSquareX = mouseX;
renderSquareY = mouseY;
}
In the first example we register our key typed function to be activated when someone types a key in our Gui. The keyCode passed can be compared with the Keyboard class keys.
In the next example we make things more complicated. We register both a draw function and a mouse clicked function. We render the same box as in the previous draw example, however, we make the coordinates of it be the last place the mouse was clicked.
Displaying the gui
To display the gui, use this
gui.open();
This opens the gui...
To close the gui, use this
gui.close();
...and this closes the gui.
These very simple methods open and close the gui, and neither take any arguments.
Inventory
The Inventory object contains methods used for getting information about the user's inventory. It can be called
through Player.getInventory()
. More about the Player
class can be found later.
Example
function hasSponge() {
const inventory = Player.getInventory();
// The ID for sponge is 19.
const spongeSlot = inventory.indexOf(19);
if (spongeSlot !== -1) {
ChatLib.chat("Sponge found in slot " + spongeSlot + "!");
} else {
ChatLib.chat("Sponge not found!");
}
}
The example above lets us see if the inventory has a sponge item in it, and if so, say the slot it's in. This works by first getting the inventory of the player, then it gets the slot ID of sponge, if there is any. If any slot has a sponge, it will output the slot, otherwise it will return -1.
KeyBinds
KeyBinds are used for detecting the state of a key.
Creation
This is the preferred method to get a keybind
const wKeyBind = Client.getKeyBindFromKey(Keyboard.KEY_W, "My W Key");
Let's break this down. We call the function Client.getKeyBindFromKey
and save the result in a variable.
This will result in trying to find an existing Minecraft keybinding in order to try not to override it. If there
isn't one being used, it will create a new KeyBind
, with the description and key we specified. We pass into this a keyCode from the Keyboard class.
In our case, this will return the keybind already used by Minecraft for the run key (unless of course you changed yours to a different key).
Using the keybind
To check if they key bind is being held, do this
if (wKeyBind.isKeyDown()) {
ChatLib.chat("Key is down!");
}
To check if the key bind was pressed, use this
if (wKeyBind.isPressed()) {
ChatLib.chat("Key is pressed!");
}
This first example would spam your chat if it was in an Tick trigger or something of the like, as it will always be true if you are holding down the key.
Now, in the second example, we would only get the chat message once every time we press and hold the key. It only returns true one time per key press. If you let go and press again, it will return true once more.
Player
The Player
object contains many methods used for retrieving information about the player.
Location
To get the direction the player is facing, use this
const direction = Player.facing();
To get the coordinates of the player, you can use this
const playerX = Player.getX();
const playerY = Player.getY();
const playerZ = Player.getZ();
Using this in conjunction with displays, you can make a coordinates HUD.
register("tick", locationTracker);
const display = new Display();
display.setRenderLoc(10, 10);
function locationTracker() {
display.setLine(0, "X: " + Player.getX());
display.setLine(1, "Y: " + Player.getY());
display.setLine(2, "Z: " + Player.getZ());
}
In this case, we just made a display, and every tick it updates to show the player's location.
LookingAt
The
LookingAt
object was replaced with thePlayer.lookingAt()
method.
This gets the current object that the player is looking at, whether that be a block or an entity. It returns
either the Block
, Sign
, Entity
class, or an air Block
when not looking at anything.
Health and Various Stats
You can not only get the player's health, hunger, active potion effects, but much more! Here's just a few examples of how to get each part.
const health = Player.getHP();
const hunger = Player.getHunger();
const potions = Player.getActivePotionEffects();
const xpLevel = Player.getXPLevel();
Armor
To get the player's armor, you can use
Player.armor
const helmet = Player.armor.getHelmet();
This would return the item that is in the helmet slot of the player. This can be a pumpkin or any item that is on the helmet slot.
Getting Inventory information
Here's an example showing how to find the durability and stack size of the item the player is holding.
function displayHeldItemInfo() {
const item = Player.getHeldItem();
if (item.getName() !== "tile.air.name") {
let durabilityPercentage = Math.ceil(
(item.getDurability() / item.getMaxDamage()) * 100
);
// If NaN, that means it's a block
if (isNaN(durabilityPercentage)) durabilityPercentage = "N/A (not a tool!)";
ChatLib.chat("Item: " + item.getName());
ChatLib.chat("Durability: " + durabilityPercentage);
ChatLib.chat("Stack Size: " + item.getStackSize());
} else {
ChatLib.chat("&4You aren't holding anything!");
}
}
In this case, we get the held item of the player. If the item isn't air, then it finds how damaged it is. If
the durability isn't a number, that means the item has to be a block. Then, if the player isn't holding anything, it
runs the last piece of the code, which is when the hand is empty.
If you want to get the player's inventory as a list of Item
s, use Player.getInventory()
.