Skip to content
Levi Webb edited this page Apr 8, 2015 · 13 revisions

Consoles API

This API is a very simplified, component-based graphics system. Everything that paints to a console (with the exception for background data) is done by components. Components can either belong to the root console, or a container (which may have layout managers, depending on the container).

Painting and updating are also separate. Repaints happen as little as possible, and are done to a buffer that is saved until the server stops. In fact, consoles are only painted once for each context (player), so if you want to update the content on your canvas consistently, you will need to call Canvas.repaint() yourself. Be careful when calling and scheduling repaints, they are expensive methods, especially for large canvases!

Updates (packets being sent to players) are handled internally, and you don't really need to touch them. These are sent much more frequently, but have minimal performance impact and are actually quite optimized. The underlying buffer for every canvas has switches for every section (item frame) that ensure that only updated sections are sent off to players. The plugin also internally removes any buffer copies when sending packets, so the only performance impact is the packet serialization.

This API is also in progress. The core design of this API is complete, but wrappers for all the default components have not been completed.

Coordinate system

The coordinate system for consoles is simple. The origin is positioned at the top left corner, with positive x and y values going across and downwards. Each item frame contains a 128 x 128 pixel area, making canvas dimensions multiples of 128.

When rendering/painting individual components, they are drawn relative to the origin of the component itself, unless CanvasGraphics.setRelative(false) is set when implementing a CanvasPainter.

Creating your own console

Creating a custom console is really easy:

Console console = new Console(BlockFace.NORTH, location, 4, 6);
console.create();

Let's break this down:

  • BlockFace.NORTH is the direction this console is facing
  • location is a bukkit location for the origin of this console
  • 4 is the width (in item frames) of our console
  • 6 is the height (again, in item frames) of the console

Finally, console.create() puts this all together and spawns the console in our world.

Creating a custom component

Components are created using a CanvasComponentBuilder, which helps make the creation of custom components more linear. There's lots you can do with component builders, so make sure to check out the API documentation as well.

Here's a simple example of a custom component:

byte[] colors = new byte[4];
CanvasComponent comp = console.newComponent(50, 60).listen((x, y, player) ->
		Bukkit.getLogger().info(String.format("%s interacted with a console at (%d, %d)", player, x, y))
).construct(component -> {
	Random random = new Random();
	for (int index = 0; index < 4; index++)
		colors[index] = (byte) (random.nextInt(139) + 4);
}).painter((g, context) -> {
	for (int x = 0; x < g.getWidth(); x++) {
		for (int y = 0; y < g.getHeight(); y++) {
			g.draw(x, y, colors[x % 4]);
		}
	}
}).create();

This component paints a pattern of vertical lines, cycling through four random colours, and listens to player interaction. Let's briefly break this down:

console.newComponent(50, 60)

This creates a new component builder for a component that is 50 x 60 pixels.

.listen((x, y, player) ->
		Bukkit.getLogger().info(String.format("%s interacted with a console at (%d, %d)", player, x, y))
)

This registers a listener that logs a message when the player interacts with the console, using a lambda.

.construct(component -> {
	Random random = new Random();
	for (int index = 0; index < 4; index++)
		colors[index] = (byte) (random.nextInt(139) + 4);
})

This registers construction code, which is ran when the component is instantiated. In this lambda, we select four random colors and store them in the colors byte array. There are 139 colors for maps in minecraft, and they are offset

.painter((g, context) -> {
	for (int x = 0; x < g.getWidth(); x++) {
		for (int y = 0; y < g.getHeight(); y++) {
			g.draw(x, y, colors[x % 4]);
		}
	}
})

And here's where the work is done. This registers a CanvasPainter for this component builder. In this painter, we use CanvasGraphis's methods, getHeight() and getWidth(), to iterate through the component's area. Then we use draw(x, y, colors[x % 4]) to draw at position (x, y) with the given color.

There's a lot more useful methods in the CanvasGraphics instance, so make sure to check those out.

We finish off our component with create(), and that's it! You may have noticed that we use bytes for colors, this is because the maps in Minecraft use a small set of ids to represent colors.

Once you've created your new custom component, you can add it to the console by accessing its Canvas:

console.getCanvas().putComponent(10, 20, comp);

This would place your new component at (10, 20) on your console, all you need to call is console.repaint(), and you're good to go!

Clone this wiki locally