Skip to content

API documentation

tom5454 edited this page Feb 7, 2025 · 22 revisions

CPM Api documentation

Table of contents:

Setup

Add the CPM api to your gradle build script (build.gradle):

Manual

Download the api from Releases and put it into your mod dev folder.
Add it to your gradle file

dependencies {
	implementation files("CustomPlayerModels-API-${cpm_api_version}.jar");
}

Gradle

Repositories

repositories {
  maven {
    name = "tom5454 maven"
    url = "https://raw.githubusercontent.com/tom5454/maven/main"
  }
}

Latest versions

API version: API version badge

Minecraft Version ID (Actual MC Version) Runtime version (Forge) Runtime version (NeoForge) Runtime version (Fabric) Runtime version (Quilt)
1.21.4 1.21.4 forge version badge 1.21.4 neoforge version badge 1.21.4 fabric version badge -
1.21.3 1.21.3 forge version badge 1.21.3 neoforge version badge 1.21.3 fabric version badge -
1.21, (1.21.1) 1.21 forge version badge 1.21 neoforge version badge 1.21 fabric version badge 1.21 quilt version badge
1.20.6 1.20.6 forge version badge 1.20.6 neoforge version badge 1.20.6 fabric version badge Use the Fabric version
1.20.4 1.20.4 forge version badge 1.20.4 neoforge version badge 1.20.4 fabric version badge 1.20.4 quilt version badge
1.20.2 1.20.2 forge version badge 1.20.2 neoforge version badge 1.20.2 fabric version badge Use the Fabric version
1.20, (1.20.1) 1.20 forge version badge Use the Forge version 1.20 fabric version badge 1.20 quilt version badge
1.19.4 1.19.4 forge version badge - 1.19.4 fabric version badge Use the Fabric version
1.19.3 1.19.3 forge version badge - 1.19.3 fabric version badge Use the Fabric version
1.19, (1.19.2) 1.19 forge version badge - 1.19 fabric version badge Use the Fabric version
1.18, (1.18.2) 1.18 forge version badge - 1.18 fabric version badge Use the Fabric version
1.17, (1.17.1) 1.17 forge version badge - 1.17 fabric version badge -
1.16, (1.16.5) 1.16 forge version badge - 1.16 fabric version badge -
1.15, (1.15.2) 1.15 forge version badge - 1.15 fabric version badge -
1.14, (1.14.4) 1.14 forge version badge - 1.14 fabric version badge -
1.12.2 1.12 forge version badge - - -
1.10.2 1.10 forge version badge - - -
1.8 1.8 forge version badge - - -
1.7.10 1.7 forge version badge - - -
1.6.4 1.6 forge version badge - - -
1.5.2 1.5 forge version badge - - -
1.4.7 1.4 forge version badge - - -
1.2.5 1.2 forge version badge - - -
b1.7.3 - - b1.7.3 babric version badge -
BTA - - BTA babric version badge -

gradle.properties

# CPM versions
cpm_api_version=<api version>
cpm_mc_version=<minecraft version>
cpm_runtime_version=<runtime version>

Dependencies using FG2

dependencies {
  compile "com.tom5454.cpm:CustomPlayerModels-API:${project.cpm_api_version}"
  deobfProvided "com.tom5454.cpm:CustomPlayerModels-${project.cpm_mc_version}:${project.cpm_runtime_version}"
}

Dependencies using FG3+

dependencies {
  /* minecraft dependency is here */

  compileOnly "com.tom5454.cpm:CustomPlayerModels-API:${project.cpm_api_version}"
  runtimeOnly fg.deobf("com.tom5454.cpm:CustomPlayerModels-${project.cpm_mc_version}:${project.cpm_runtime_version}")
}

Dependencies using NeoGradle

dependencies {
  /* minecraft dependency is here */

  compileOnly "com.tom5454.cpm:CustomPlayerModels-API:${project.cpm_api_version}"
  runtimeOnly "com.tom5454.cpm:CustomPlayerModels-${project.cpm_mc_version}:${project.cpm_runtime_version}"
}

Dependencies using Fabric

dependencies {
  /* minecraft dependency is here */
  
  compileOnly "com.tom5454.cpm:CustomPlayerModels-API:${project.cpm_api_version}"
  modRuntimeOnly "com.tom5454.cpm:CustomPlayerModels-Fabric-${project.cpm_mc_version}:${project.cpm_runtime_version}"
}

Dependencies using Voldeloom

dependencies {
  /* minecraft dependency is here */
  
  compileOnly "com.tom5454.cpm:CustomPlayerModels-API:${project.cpm_api_version}"
  //1.4.7, 1.5.2
  coremodImplementation ("com.tom5454.cpm:CustomPlayerModels-${project.cpm_mc_version}:${project.cpm_runtime_version}") {
    copyToFolder("coremods")
  }
  //1.6.4
  modRuntimeOnly "com.tom5454.cpm:CustomPlayerModels-${project.cpm_mc_version}:${project.cpm_runtime_version}"
}

On 1.2.5:

volde {
	runs {
		client {
			programArg "Dev"
			vmArg "-Dcpmcore.deobf=true"
			vmArg "-Dcpmcore.env.client=true"
			vmArg "-javaagent:\"" + file("CustomPlayerModels-${project.cpm_runtime_version}.jar").absolutePath + "\""
		}
	}
}

dependencies {
  /* minecraft dependency is here */
  
  compileOnly "com.tom5454.cpm:CustomPlayerModels-API:${project.cpm_api_version}"
}

You have to place the current mod version from Modrinth to the project root folder, or the Java Agent won't load.

Create your Plugin

Create a class implementing ICPMPlugin.

public class CPMCompat implements ICPMPlugin {
	public void initClient(IClientAPI api) {
		//Init client
	}
	
	public void initCommon(ICommonAPI api) {
		//Init common
	}
	
	public String getOwnerModId() {
		return "example_mod";
	}
}

Forge 1.2.5

The plugin loader isn't implemented yet.

Forge 1.12 and lower

Send an IMC message with your plugin class location.
FMLInterModComms.sendMessage("customplayermodels", "api", "com.example.mod.CPMCompat");

Forge 1.16 and up

Send an IMC message.

public MyMod() {
	...
	FMLJavaModLoadingContext.get().getModEventBus().addListener(this::enqueueIMC);
	...
}
	
private void enqueueIMC(final InterModEnqueueEvent event)  {
	InterModComms.sendTo("cpm", "api", () -> (Supplier<?>) () -> new CPMCompat());
	...
}

Fabric

Register your plugin class as entry point in your fabric.mod.json as such:

"entrypoints": {
    "cpmapi": [ "com.example.mod.CPMCompat" ]
}

Bukkit, Spigot, Paper (0.4.1+)

Register your plugin using the service manager, your plugin's initCommon method will be called with the ICommonAPI instance.

RegisteredServiceProvider<CPMPluginRegistry> rsp = getServer().getServicesManager().getRegistration(CPMPluginRegistry.class);
if (rsp != null)
	rsp.getProvider().register(new CPMCompat());
else
	log.info("Customizable Player Models plugin not installed, compat disabled");

Client API

Voice animation

Register a voice level supplier.
IClientAPI:registerVoice(Player.class, player -> voiceLevel);
Player.class

Register a voice level supplier. (UUID variant) (0.6.0+)
IClientAPI:registerVoice(playerUUID -> voiceLevel);

Register a voice muted supplier. (0.6.0+)
IClientAPI:registerVoiceMute(Player.class, player -> voiceMuted);
Player.class

Register a voice muted supplier. (UUID variant) (0.6.0+)
IClientAPI:registerVoiceMute(playerUUID -> voiceMuted);

Rendering API

Create a player renderer to render CPM models on any Humanoid Entity.
PlayerRenderer<Model, ResourceLocation, RenderType, MultiBufferSource, GameProfile> renderer = IClientAPI.createPlayerRenderer(Model.class, ResourceLocation.class, RenderType.class, MultiBufferSource.class, GameProfile.class)
For 1.12 and lower use:
RetroPlayerRenderer<Model, GameProfile> renderer = IClientAPI.createPlayerRenderer(Model.class, GameProfile.class);
Model.class
ResourceLocation.class
RenderType.class
MultiBufferSource.class
GameProfile.class

Rendering an Entity with CPM model

  1. Using the renderer set the GameProfile or LocalModel before rendering.
    setGameProfile(gameProfile): Render player model
    setLocalModel(localModel): Render a local model, Loading a local model
  2. Set the base model, must be a Humanoid or Biped model: setRenderModel(model)
  3. Set the default RenderType factory on 1.16+: e.g.: translucent entity: setRenderType(RenderType::entityTranslucent).
  4. Pose the model, apply animations to the model using getAnimationState(), setActivePose(pose), setActiveGesture(gesture).
  5. Call preRender(buffers, mode) (or preRender(mode), on 1.12-)
  6. Render your model normally. CPM has injected it's renderer into your model. (Use getDefaultRenderType() for getting the RenderType for the model (1.16+)).
    1. To render additional parts (elytra, cape, armor) call: prepareSubModel(model, type, texture) (or prepareSubModel(model, type) on 1.12-)
    2. Optionally on 1.16+ change the default RenderType for the part: e.g.: setRenderType(RenderType::armorCutoutNoCull)
    3. Render your part (Use getRenderTypeForSubModel(subModel) for getting the RenderType for the model (1.16+)).
  7. Call postRender() to finish rendering.

Example (1.18 Forge):

import java.io.IOException;
import java.io.InputStream;

import net.minecraft.client.Minecraft;
import net.minecraft.client.model.Model;
import net.minecraft.client.model.PlayerModel;
import net.minecraft.client.model.geom.ModelLayers;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRendererProvider.Context;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.resources.ResourceLocation;

import com.mojang.authlib.GameProfile;
import com.mojang.blaze3d.vertex.PoseStack;

import com.tom.cpm.api.IClientAPI;
import com.tom.cpm.api.IClientAPI.PlayerRenderer;
import com.tom.cpm.shared.animation.AnimationEngine.AnimationMode;

public class ExampleRenderer extends LivingEntityRenderer<ExampleEntity, PlayerModel<ExampleEntity>> {
	private static PlayerRenderer<Model, ResourceLocation, RenderType, MultiBufferSource, GameProfile> renderer;

	public static void init(IClientAPI api) {
		renderer = api.createPlayerRenderer(Model.class, ResourceLocation.class, RenderType.class, MultiBufferSource.class, GameProfile.class);
		
		// TODO: replace with resource reload listeners to support resourcepacks.
		try (InputStream is = Minecraft.getInstance().getResourceManager().open(new ResourceLocation("example_mod", "models/example_entity_model.cpmmodel"))){
			renderer.setLocalModel(api.loadModel("example_entity_model", is));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public ExampleRenderer(Context pContext, float pShadowRadius) {
		super(pContext, new PlayerModel<>(pContext.bakeLayer(ModelLayers.PLAYER), false), pShadowRadius);

	}

	@Override
	public void render(ExampleEntity pEntity, float pEntityYaw, float pPartialTicks, PoseStack pMatrixStack,
			MultiBufferSource pBuffer, int pPackedLight) {
		renderer.setRenderModel(model);
		renderer.setRenderType(RenderType::entityTranslucent);
		//Pose model using renderer.getAnimationState(), setActivePose(name) or setActiveGesture(name)
		renderer.preRender(pBuffer, AnimationMode.PLAYER);
		if(renderer.getDefaultTexture() != null) {
			super.render(pEntity, pEntityYaw, pPartialTicks, pMatrixStack, pBuffer, pPackedLight);
		} else {
			renderNameTag(pEntity, pEntity.getDisplayName(), pMatrixStack, pBuffer, pPackedLight);
		}
		renderer.postRender();
	}

	@Override
	public ResourceLocation getTextureLocation(ExampleEntity pEntity) {
		return renderer.getDefaultTexture();
	}
}

While the model is loading in the background renderer.getDefaultTexture() will return null!

Loading a local model

Load a model from a .cpmmodel file.
IClientAPI.loadModel(name, inputstream)
Use the loaded model for Rendering an Entity

Register Editor Generator

Register a model generator for the editor. Generators are under Edit/Tools.
IClientAPI.registerEditorGenerator("button.example_mod.example_generator", "tooltip.example_mod.example_generator", ExampleGenerator::apply);

public class ExampleGenerator {
	public static void apply(EditorGui gui) {
		//TODO: apply the generator
		// Use gui.getEditor() to access the editor
		// Use Editor.action and ActionBuilder to make undoable changes.
		// Note: parts of the editor may change.
	}
}

Localization:
Add button.example_mod.example_generator, tooltip.example_mod.example_generator to your language file. Use \ characters for line breaks in the tooltip.

Play Animation (0.6.0+)

Play the given command animation for a player (Client-side).

Name: Animation name
IClientAPI.playAnimation(name); or
Value: 0: reset pose/gesture, 1: play pose/gesture, for layers value: 0-255, toggle: 0-1 or -1 to switch state IClientAPI.playAnimation(name, value);
Returns: true if the animation was found and started playing

Detect Animation (0.6.14+)

Detect if an animation is playing for the player
int value = IClientAPI.getAnimationPlaying(name);
Returns: The animation value (value layer: 0-255, other animations: 0-1), -1 if animation doesn't exist

Get Animation Maximum Value (0.6.20+)

Get the max value for the given animation (value or toggle)
int value = IClientAPI.getAnimationMaxValue(name);
Returns: maximum value for layers (value set in the animation 0-255, toggle: always 1, -1 if animation doesn't exist or isn't a layer)

Client Networking (0.6.1+)

Register a NBT message to send to the server, or broadcast it to other clients.
MessageSender sender = IClientAPI.registerPluginMessage(Player.class, message_id, (player, message) -> {/*Handle message*/}, broadcastToTracking);
Player.class
or UUID version:
MessageSender sender = IClientAPI.registerPluginMessage(message_id, (player_uuid, message) -> {/*Handle message*/}, broadcastToTracking);
Use the MessageSender to send messages.
boolean success = sender.sendMessage(message_tag);
Use the platform independent NBT implementation from com.tom.cpl.nbt.* package.
broadcastToTracking: false: Send the message to the server / true: broadcast message to nearby players (through the server).
CPM 0.6.1+ is required on the server side for the networking to work.
When using broadcastToTracking, or State Messages your mod/plugin is not required on the server for the packet forwarding to work, you don't have to register anything. To receive non broadcast messages use the ICommonAPI.registerPluginMessage

State Messages

The last state message is stored on the server and sent to every client that enters the tracking range (render distance).
MessageSender sender = IClientAPI.registerPluginStateMessage(Player.class, message_id, (player, message) -> {/*Handle message*/});
Player.class
or UUID version:
MessageSender sender = IClientAPI.registerPluginMessage(message_id, (player_uuid, message) -> {/*Handle message*/});

Class Map

Classes are dependent on your minecraft version and mod loader.

Client Player.class

Minecraft Forge 1.12 and lower: EntityPlayer.class
Minecraft Forge 1.16 and Fabric: PlayerEntity.class
Minecraft Forge 1.17 and up: Player.class from net.minecraft.*

Client Model.class

Minecraft Forge 1.16+ and Fabric: Model.class from net.minecraft.client.*
Minecraft Forge 1.12 and lower: ModelBase.class

Client ResourceLocation.class

Minecraft Forge: ResourceLocation.class
Fabric: Identifier.class

Client RenderType.class

Minecraft Forge: RenderType.class
Fabric: RenderLayer.class

Client MultiBufferSource.class

Minecraft Forge 1.16: IRenderTypeBuffer.class
Minecraft Forge (1.17+): MultiBufferSource.class
Fabric: VertexConsumerProvider.class

Client GameProfile.class

GameProfile from AuthLib: com.mojang.authlib.GameProfile

Common API

Set Model (0.4.1+)

Set the player model
ICommonAPI.setPlayerModel(Player.class, playerObj, base64Model, forced, persistent);
or
ICommonAPI.setPlayerModel(Player.class, playerObj, modelFile, forced);
Create a ModelFile using ModelFile.load(file); or ModelFile.load(inputstream);
or
ICommonAPI.resetPlayerModel(Player.class, playerObj);
clear the server set model
Player.class

Jump (0.4.1+)

Play the jump animation on for a player.
ICommonAPI.playerJumped(Player.class, playerObj);
Player.class

Play Animation (0.6.0+)

Play the given command animation for a player (Server-side).

Name: Animation name
ICommonAPI.playAnimation(Player.class, playerObj, name); or
ICommonAPI.playAnimation(Player.class, playerObj, name, value);
Value: 0: reset pose/gesture, 1: play pose/gesture, for layers value: 0-255, toggle: 0-1 or -1 to switch state
Player.class

Server Networking (0.6.1+)

Register a NBT message to receive non broadcast messages/send messages to clients.
MessageSender<Player> sender = ICommonAPI.registerPluginMessage(Player.class, message_id, (player, message) -> {/*Handle message*/});
Player.class
Use the MessageSender to send messages.
boolean success = sender.sendMessageTo(player, message_tag);
or broadcast to nearby players:
sender.sendMessageToTracking(player, message_tag, sendToSelf);
Use the platform independent NBT implementation from com.tom.cpl.nbt.* package.
sendToSelf: Send message to the selected player in argument 1

Detect Animation (0.6.9+)

Detect if an animation is playing for the player
int value = ICommonAPI.getAnimationPlaying(Player.class, playerObj, name);
Returns: The animation value (value layer: 0-255, other animations: 0-1), -1 if animation doesn't exist
Player.class

Class Map

Classes are dependent on your minecraft version and mod loader.

Common Player.class

Minecraft Forge 1.12 and lower: EntityPlayer.class
Minecraft Forge 1.16 and Fabric: PlayerEntity.class
Minecraft Forge 1.17 and up: Player.class from net.minecraft.*
Bukkit: Player.class from org.bukkit.entity.

Clone this wiki locally