(In BlockBench / In Minecraft)
BetterModel is a server-based engine that provides runtime BlockBench model rendering & animating for Minecraft Java Edition.
It implements fully server-side 3D models by using an item display entity packet.
- Importing Generic BlockBench model
.bbmodel - Auto-generating resource pack
- Playing animation
- Syncing with base entity
- Custom hit box
- 12-limb player animation
BetterModel aims to be a reliable engine that provides stable, high-quality animations for Paper-based high-traffic servers.
- Stability First: We take a conservative approach to feature expansion. By avoiding the implementation of features that are difficult to maintain or have limited use cases, we focus on providing a stable API and ensuring overall operational safety.
- Performance Optimized: Our goal is to minimize runtime computation, memory footprint, and network overhead. Through asynchronous design and optimized packet handling, we ensure the engine runs efficiently even under heavy server loads.
- Tailored for Large-scale Servers: We provide essential features specifically designed for high-population servers and MMORPG content creation.
- Per-player Animation: Individual animation control tailored to each player's perspective.
- Player Model Animation: Support for sophisticated 12-limb animations based on player models.
./gradlew build: Builds all jars
./gradlew shadowJar: Builds plugin jar
./gradlew javadocJar: Builds javadoc jar
./gradlew runServer: Runs Paper test server with test plugin
- Kotlin stdlib: modern functional programming
- semver4j: semver parser
- cloud: command
- adventure: component
- stable player display: player animation
- caffeine: concurrent map cache
- DynamicUV: player model
- ArmorModel: armor in player model
- molang-compiler: compiling and evaluating molang expression
- libby: runtime library downloader
Note
For more detailed API specifications, please refer to our GitHub Wiki.
Gradle (Kotlin)
repositories {
mavenCentral()
maven("https://maven.blamejared.com/") // For transitive dependency in bettermodel-fabric
maven("https://maven.nucleoid.xyz/") // For transitive dependency in bettermodel-fabric
}
dependencies {
compileOnly("io.github.toxicity188:bettermodel-bukkit-api:VERSION") // bukkit(spigot, paper, etc) api
//modApi("io.github.toxicity188:bettermodel-fabric:VERSION") // mod(fabric)
}repositories {
maven("https://maven.pkg.github.com/toxicity188/BetterModel") {
credentials {
username = YOUR_GITHUB_USERNAME
password = YOUR_GITHUB_TOKEN
}
}
maven("https://maven.blamejared.com/") // For transitive dependency in bettermodel-fabric
maven("https://maven.nucleoid.xyz/") // For transitive dependency in bettermodel-fabric
}
dependencies {
compileOnly("io.github.toxicity188:bettermodel-bukkit-api:VERSION-SNAPSHOT") // bukkit(spigot, paper, etc) api
//modApi("io.github.toxicity188:bettermodel-fabric:VERSION-SNAPSHOT") // mod(fabric)
}Gradle (Groovy)
repositories {
mavenCentral()
maven 'https://maven.blamejared.com/' // For transitive dependency in bettermodel-fabric
maven 'https://maven.nucleoid.xyz/' // For transitive dependency in bettermodel-fabric
}
dependencies {
compileOnly 'io.github.toxicity188:bettermodel-bukkit-api:VERSION' // bukkit(spigot, paper, etc) api
//modApi 'io.github.toxicity188:bettermodel-fabric:VERSION' // mod(fabric)
}repositories {
maven {
url "https://maven.pkg.github.com/toxicity188/BetterModel"
credentials {
username = YOUR_GITHUB_USERNAME
password = YOUR_GITHUB_TOKEN
}
}
maven 'https://maven.blamejared.com/' // For transitive dependency in bettermodel-fabric
maven 'https://maven.nucleoid.xyz/' // For transitive dependency in bettermodel-fabric
}
dependencies {
compileOnly 'io.github.toxicity188:bettermodel-bukkit-api:VERSION-SNAPSHOT' // bukkit(spigot, paper, etc) api
//modApi 'io.github.toxicity188:bettermodel-fabric:VERSION-SNAPSHOT' // mod(fabric)
}Maven
<repositories> <repository> <id>central</id> <url>https://repo.maven.apache.org/maven2</url> </repository> </repositories> <dependencies> <dependency> <groupId>io.github.toxicity188</groupId> <artifactId>bettermodel-bukkit-api</artifactId> <version>VERSION</version> <scope>provided</scope> </dependency> </dependencies>
<repositories> <repository> <id>github</id> <url>https://maven.pkg.github.com/toxicity188/BetterModel</url> </repository> </repositories> <dependencies> <dependency> <groupId>io.github.toxicity188</groupId> <artifactId>bettermodel-api</artifactId> <version>VERSION-SNAPSHOT</version> <scope>provided</scope> </dependency> <dependency> <groupId>io.github.toxicity188</groupId> <artifactId>bettermodel-bukkit-api</artifactId> <version>VERSION-SNAPSHOT</version> <scope>provided</scope> </dependency> </dependencies>
Example code
BetterModel.model("demon_knight"); //A model file in BetterModel/models (for general model with saving) BetterModel.limb("steve"); //A model file in BetterModel/players (for player model with no saveing) BetterModel.modelOrNull("demon_knight"); //general model or null BetterModel.limbOrNull("steve"); //player model or null
EntityTracker tracker = BetterModel.model("demon_knight") .map(r -> r.getOrCreate(BukkitAdapter.adapt(entity))) //Gets or creates entity tracker by this renderer to some entity. .orElse(null);
EntityTracker tracker = BetterModel.model("demon_knight") .map(r -> r.create(BukkitAdapter.adapt(entity), TrackerModifier.DEFAULT, t -> t.update(TrackerUpdateAction.tint(0x0026FF)))) //Creates entity tracker with pre-spawn task. .orElse(null);
DummyTracker tracker = BetterModel.model("demon_knight") .map(r -> r.create(BukkitAdapter.adapt(location))) //Creates some dummy tracker to this location. .orElse(null);
DummyTracker tracker = BetterModel.limb("steve") .map(r -> r.create(BukkitAdapter.adapt(location), ModelProfile.of(BukkitAdapter.adapt(player)))) //Creates some dummy tracker to this location and player's skin profile. .orElse(null);
BetterModel.model("demon_knight") .map(r -> r.create(BukkitAdapter.adapt(entity), TrackerModifier.DEFAULT, t -> { t.update(TrackerUpdateAction.tint(rgb)); //Tint t.update(TrackerUpdateAction.enchant(true), bone -> true); //Enchant with predicate })) .ifPresent(tracker -> tracker.update(TrackerUpdateAction.composite( //Composite TrackerUpdateAction.brightness(15, 15) //Brightness TrackerUpdateAction.billboard(Display.Billboard.CENTER) //Billboard ))); }