Skip to content

Instantly share code, notes, and snippets.

@ZeroErrors
Created June 17, 2026 22:10
Show Gist options
  • Select an option

  • Save ZeroErrors/6d29be75d9969231caa532a8758e5bb4 to your computer and use it in GitHub Desktop.

Select an option

Save ZeroErrors/6d29be75d9969231caa532a8758e5bb4 to your computer and use it in GitHub Desktop.
package dev.zero.scaleeffect;
import com.hypixel.hytale.component.Component;
import com.hypixel.hytale.component.ComponentType;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import javax.annotation.Nonnull;
public class ScaleAnimation implements Component<EntityStore> {
private static ComponentType<EntityStore, ScaleAnimation> componentType;
public static ComponentType<EntityStore, ScaleAnimation> getComponentType() {
return componentType;
}
public static void setComponentType(@Nonnull ComponentType<EntityStore, ScaleAnimation> type) {
componentType = type;
}
private float startScale = 1f;
private float targetScale = 1f;
private float duration = 1f;
private float elapsed = 0f;
public ScaleAnimation() {
}
public ScaleAnimation(float startScale, float targetScale, float duration) {
this.startScale = startScale;
this.targetScale = targetScale;
this.duration = duration;
}
public float getStartScale() {
return startScale;
}
public float getTargetScale() {
return targetScale;
}
public float getDuration() {
return duration;
}
public float getElapsed() {
return elapsed;
}
public void advance(float dt) {
elapsed += dt;
}
@Nonnull
@Override
public Component<EntityStore> clone() {
var copy = new ScaleAnimation(startScale, targetScale, duration);
copy.elapsed = elapsed;
return copy;
}
}
package dev.zero.scaleeffect;
import com.hypixel.hytale.component.ArchetypeChunk;
import com.hypixel.hytale.component.CommandBuffer;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.component.query.Query;
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
import com.hypixel.hytale.server.core.asset.type.model.config.Model;
import com.hypixel.hytale.server.core.modules.entity.component.EntityScaleComponent;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import com.hypixel.hytale.server.core.modules.entity.player.PlayerSkinComponent;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import javax.annotation.Nonnull;
public class ScaleAnimationSystem extends EntityTickingSystem<EntityStore> {
private static final float MIN_SCALE = 0.01f;
private final Query<EntityStore> query = Query.and(
ScaleAnimation.getComponentType(),
ModelComponent.getComponentType()
);
@Nonnull
@Override
public Query<EntityStore> getQuery() {
return query;
}
@Override
public void tick(float dt, int index, @Nonnull ArchetypeChunk<EntityStore> chunk, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer) {
var animation = chunk.getComponent(index, ScaleAnimation.getComponentType());
var modelComponent = chunk.getComponent(index, ModelComponent.getComponentType());
if (animation == null || modelComponent == null) return;
animation.advance(dt);
float t = Math.min(animation.getElapsed() / animation.getDuration(), 1f);
float eased = t * t * (3f - 2f * t);
float scale = animation.getStartScale() + (animation.getTargetScale() - animation.getStartScale()) * eased;
scale = Math.max(scale, MIN_SCALE);
var ref = chunk.getReferenceTo(index);
// Two scales drive a player: EntityScaleComponent is the rendered size, the model
// scale is the camera, eye height, and hitbox. Set both so they shrink together.
var scaleComponent = chunk.getComponent(index, EntityScaleComponent.getComponentType());
if (scaleComponent != null) {
scaleComponent.setScale(scale);
} else {
commandBuffer.putComponent(ref, EntityScaleComponent.getComponentType(), new EntityScaleComponent(scale));
}
var reference = modelComponent.getModel().toReference();
var scaledModel = new Model.ModelReference(reference.getModelAssetId(), scale,
reference.getRandomAttachmentIds(), reference.isStaticModel()).toModel();
if (scaledModel != null) {
commandBuffer.putComponent(ref, ModelComponent.getComponentType(), new ModelComponent(scaledModel));
// Rebuilding the model resends the default texture, so re-flag the skin to keep it applied.
var skin = chunk.getComponent(index, PlayerSkinComponent.getComponentType());
if (skin != null) skin.setNetworkOutdated();
}
if (t >= 1f) {
commandBuffer.tryRemoveComponent(ref, ScaleAnimation.getComponentType());
}
}
}
package dev.zero.scaleeffect;
import com.hypixel.hytale.builtin.triggervolumes.effect.TriggerContext;
import com.hypixel.hytale.builtin.triggervolumes.effect.TriggerEffect;
import com.hypixel.hytale.codec.Codec;
import com.hypixel.hytale.codec.KeyedCodec;
import com.hypixel.hytale.codec.builder.BuilderCodec;
import com.hypixel.hytale.server.core.modules.entity.component.ModelComponent;
import javax.annotation.Nonnull;
public class ScaleEffect extends TriggerEffect {
@Nonnull
public static final BuilderCodec<ScaleEffect> CODEC = BuilderCodec.builder(ScaleEffect.class, ScaleEffect::new, BASE_CODEC)
.append(
new KeyedCodec<>("TargetScale", Codec.FLOAT, false),
(e, v) -> e.targetScale = v,
e -> e.targetScale
).add()
.append(
new KeyedCodec<>("Duration", Codec.FLOAT, false),
(e, v) -> e.duration = v,
e -> e.duration
).add()
.build();
private float targetScale = 0.25f;
private float duration = 3.0f;
@Override
public void execute(@Nonnull TriggerContext context) {
var ref = context.getEntityRef();
var store = context.getStore();
var modelComponent = store.getComponent(ref, ModelComponent.getComponentType());
if (modelComponent == null) return;
float startScale = modelComponent.getModel().getScale();
if (startScale <= 0f) startScale = 1f;
float resolvedDuration = duration > 0f ? duration : 0.0001f;
store.putComponent(ref, ScaleAnimation.getComponentType(), new ScaleAnimation(startScale, targetScale, resolvedDuration));
}
}
package dev.zero.scaleeffect;
import com.hypixel.hytale.builtin.triggervolumes.effect.TriggerEffect;
import com.hypixel.hytale.protocol.ClientCameraView;
import com.hypixel.hytale.protocol.packets.camera.SetServerCamera;
import com.hypixel.hytale.server.core.event.events.BootEvent;
import com.hypixel.hytale.server.core.event.events.player.PlayerReadyEvent;
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import javax.annotation.Nonnull;
import java.util.logging.Level;
public class ScaleEffectPlugin extends JavaPlugin {
public ScaleEffectPlugin(@Nonnull JavaPluginInit init) {
super(init);
}
@Override
protected void setup() {
ScaleAnimation.setComponentType(getEntityStoreRegistry().registerComponent(ScaleAnimation.class, ScaleAnimation::new));
getEntityStoreRegistry().registerSystem(new ScaleAnimationSystem());
TriggerEffect.CODEC.register("Scale", ScaleEffect.class, ScaleEffect.CODEC);
// Note: This is broken atm, prevents interactions and crashes if reset via `/player camera reset`. Fixed in 0.6.0-pre.4
// getEventRegistry().registerGlobal(PlayerReadyEvent.class, event -> {
// var ref = event.getPlayerRef();
// var playerRef = ref.getStore().getComponent(ref, PlayerRef.getComponentType());
//
// playerRef.getPacketHandler().writeNoCache(new SetServerCamera(ClientCameraView.ThirdPerson, false, null));
// });
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment