Getting started
This page builds a small plugin from zero. At the end, a sword spins and floats above you when you type a command.
You need Paper 26.3, Java 25 and the PacketEvents plugin (2.14.0 or newer) on the server. PacketEvents is a separate plugin. The server owner installs it. You do not put it in your jar.
What is displaymotion?#
Minecraft has three display entities: item, block and text displays. They show an item, a block or a text in the world. They can move, turn and change size.
displaymotion makes this easy. You say what should move, and the library sends the right packets to the players. There are no real entities on the server, so it does not slow the server down. Players need no mod and no resource pack.
1. Add the library#
Make a Gradle project. This is the full build.gradle.kts:
plugins {
java
id("com.gradleup.shadow") version "9.6.1"
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
}
repositories {
mavenCentral()
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://repo.codemc.io/repository/maven-releases/")
maven("https://jitpack.io")
}
dependencies {
compileOnly("io.papermc.paper:paper-api:26.3.build.180-stable")
compileOnly("com.github.retrooper:packetevents-spigot:2.14.0")
implementation("com.github.Jan1k1.displaymotion:displaymotion-paper:v0.2.0")
}
tasks.shadowJar {
relocate("org.jan1k.displaymotion", "your.plugin.libs.displaymotion")
}
What the parts do:
compileOnlymeans: use it to build, but do not put it in the jar. Paper and PacketEvents are on the server already.implementationmeans: put it in the jar. This is displaymotion.relocatemoves the displaymotion classes into your own package. Then two plugins that both use displaymotion never clash. Changeyour.plugin.libs.displaymotionto a package that fits your plugin.v0.2.0is the newest release.main-SNAPSHOTgives the newest commit instead.
Do not shade PacketEvents. The server installs it.
2. plugin.yml#
Make the file src/main/resources/plugin.yml:
name: MyPlugin
version: "1.0"
main: your.plugin.MyPlugin
api-version: "26.3"
depend: [packetevents]
commands:
sword:
description: Shows a spinning sword
depend: [packetevents] is important. It makes the server load PacketEvents first. Without it, DisplayMotion.create fails.
3. First display#
Make the file src/main/java/your/plugin/MyPlugin.java:
package your.plugin;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.java.JavaPlugin;
import org.jan1k.displaymotion.Animation;
import org.jan1k.displaymotion.Animations;
import org.jan1k.displaymotion.paper.DisplayMotion;
import org.jan1k.displaymotion.paper.MotionDisplay;
public final class MyPlugin extends JavaPlugin {
private static final Animation SHOW = Animations.popIn(20)
.then(Animations.spin(80).with(Animations.bob(0.3f, 80)));
private DisplayMotion motion;
@Override
public void onEnable() {
motion = DisplayMotion.create(this);
getCommand("sword").setExecutor((sender, command, label, args) -> {
if (!(sender instanceof Player player)) {
sender.sendMessage("Players only.");
return true;
}
showSword(player);
return true;
});
}
private void showSword(Player player) {
MotionDisplay sword = motion.item(player.getLocation().add(0, 1.5, 0), ItemStack.of(Material.DIAMOND_SWORD));
sword.play(SHOW);
}
}
What happens in the code:
DisplayMotion.create(this)starts the library. It is your entry point for everything.motion.item(location, item)makes an item display at the location. It returns aMotionDisplay. This is a handle to the display.SHOWis an animation.popIn(20)grows the item from nothing in 20 ticks (1 second). Then it spins, one turn every 80 ticks, and moves up and down at the same time.thenmeans "after that".withmeans "at the same time".sword.play(SHOW)starts the animation.
That is all. The library removes every display when your plugin disables.
4. Build and run#
- Run
./gradlew shadowJar. The jar is inbuild/libs/. - Copy the jar to the
pluginsfolder of a Paper 26.3 server. Put the PacketEvents jar there too. - Start the server and join.
- Type
/sword.
You should see a diamond sword grow out of nothing, about one and a half blocks above your feet. Then it spins and floats up and down. It stays until you remove it or the plugin stops. To remove it, call sword.remove(). If you walk more than 48 blocks away, it hides, and it comes back when you return.
If something is wrong:
| What you see | Fix |
|---|---|
PacketEvents is not running in the log |
Install PacketEvents on the server. Check that depend: [packetevents] is in plugin.yml. |
Unknown command |
The command is not in plugin.yml, or the plugin did not load. Read the log. |
| The sword does not show | You are more than 48 blocks away, or your client is older than 1.19.4. |
Rules to remember#
- Call
DisplayMotion.createonce, on the main thread, inonEnable. - Keep animations in
static finalfields. Each animation object is baked once. - Call
playright after spawning, in the same tick. Then the first frame is already the animation. - A display never moves by itself. Use
teleportto move it, and animations to move parts.
Where to go next#
- Animations: make your own movements.
- Models: build things from many blocks, items and texts.
- Effects: kill effects in one line.
- Importing: draw a model in Blockbench or BDEngine and load it.
- Cookbook: short recipes to copy.