-
-
Notifications
You must be signed in to change notification settings - Fork 55
API documentation
Table of contents:
Add the CPM api to your gradle build script (build.gradle
):
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");
}
repositories {
maven {
name = "tom5454 maven"
url = "https://raw.githubusercontent.com/tom5454/maven/main"
}
}
API version:
Minecraft Version ID (Actual MC Version) | Runtime version (Forge) | Runtime version (NeoForge) | Runtime version (Fabric) | Runtime version (Quilt) |
---|---|---|---|---|
1.21.4 | - | |||
1.21.3 | - | |||
1.21, (1.21.1) | ||||
1.20.6 | Use the Fabric version | |||
1.20.4 | ||||
1.20.2 | Use the Fabric version | |||
1.20, (1.20.1) | Use the Forge version | |||
1.19.4 | - | Use the Fabric version | ||
1.19.3 | - | Use the Fabric version | ||
1.19, (1.19.2) | - | Use the Fabric version | ||
1.18, (1.18.2) | - | Use the Fabric version | ||
1.17, (1.17.1) | - | - | ||
1.16, (1.16.5) | - | - | ||
1.15, (1.15.2) | - | - | ||
1.14, (1.14.4) | - | - | ||
1.12.2 | - | - | - | |
1.10.2 | - | - | - | |
1.8 | - | - | - | |
1.7.10 | - | - | - | |
1.6.4 | - | - | - | |
1.5.2 | - | - | - | |
1.4.7 | - | - | - | |
1.2.5 | - | - | - | |
b1.7.3 | - | - | - | |
BTA | - | - | - |
# CPM versions
cpm_api_version=<api version>
cpm_mc_version=<minecraft version>
cpm_runtime_version=<runtime version>
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 {
/* 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 {
/* 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 {
/* 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 {
/* 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 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";
}
}
The plugin loader isn't implemented yet.
Send an IMC message with your plugin class location.
FMLInterModComms.sendMessage("customplayermodels", "api", "com.example.mod.CPMCompat");
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());
...
}
Register your plugin class as entry point in your fabric.mod.json as such:
"entrypoints": {
"cpmapi": [ "com.example.mod.CPMCompat" ]
}
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");
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);
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
- 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 - Set the base model, must be a Humanoid or Biped model:
setRenderModel(model)
- Set the default RenderType factory on 1.16+: e.g.: translucent entity:
setRenderType(RenderType::entityTranslucent)
. - Pose the model, apply animations to the model using
getAnimationState()
,setActivePose(pose)
,setActiveGesture(gesture)
. - Call
preRender(buffers, mode)
(orpreRender(mode)
, on 1.12-) - Render your model normally. CPM has injected it's renderer into your model. (Use
getDefaultRenderType()
for getting the RenderType for the model (1.16+)).- To render additional parts (elytra, cape, armor) call:
prepareSubModel(model, type, texture)
(orprepareSubModel(model, type)
on 1.12-) - Optionally on 1.16+ change the default RenderType for the part: e.g.:
setRenderType(RenderType::armorCutoutNoCull)
- Render your part (Use
getRenderTypeForSubModel(subModel)
for getting the RenderType for the model (1.16+)).
- To render additional parts (elytra, cape, armor) call:
- 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!
Load a model from a .cpmmodel
file.
IClientAPI.loadModel(name, inputstream)
Use the loaded model for Rendering an Entity
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 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 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 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)
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
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*/});
Classes are dependent on your minecraft version and mod loader.
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.*
Minecraft Forge 1.16+ and Fabric: Model.class
from net.minecraft.client.*
Minecraft Forge 1.12 and lower: ModelBase.class
Minecraft Forge: ResourceLocation.class
Fabric: Identifier.class
Minecraft Forge: RenderType.class
Fabric: RenderLayer.class
Minecraft Forge 1.16: IRenderTypeBuffer.class
Minecraft Forge (1.17+): MultiBufferSource.class
Fabric: VertexConsumerProvider.class
GameProfile from AuthLib: com.mojang.authlib.GameProfile
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
Play the jump animation on for a player.
ICommonAPI.playerJumped(Player.class, playerObj);
Player.class
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
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 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
Classes are dependent on your minecraft version and mod loader.
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
.
Customizable Player Models Wiki