Implement environment attribute easing types

This commit is contained in:
booky committed 2025-12-07 22:39:10 +01:00
1 parent 3c8079aee2
commit fe29db24cf
30 files changed
+671 -51

No files matched your search

@@ -28,7 +28,7 @@ import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* @version 1.21.9+
* @versions 1.21.9+
*/
@NullMarked
public class ParticleSpellData extends ParticleData {
@@ -25,6 +25,7 @@ import org.jspecify.annotations.NullMarked;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
@NullMarked
public interface NbtCodec<T> extends NbtEncoder<T>, NbtDecoder<T> {
@@ -43,6 +44,27 @@ public interface NbtCodec<T> extends NbtEncoder<T>, NbtDecoder<T> {
};
}
default NbtCodec<T> validate(Predicate<T> predicate) {
return new NbtCodec<T>() {
@Override
public T decode(NBT nbt, PacketWrapper<?> wrapper) throws NbtCodecException {
T val = NbtCodec.this.decode(nbt, wrapper);
if (!predicate.test(val)) {
throw new NbtCodecException("Decode predicate failed " + predicate);
}
return val;
}
@Override
public NBT encode(PacketWrapper<?> wrapper, T value) throws NbtCodecException {
if (!predicate.test(value)) {
throw new NbtCodecException("Encode predicate failed " + predicate);
}
return NbtCodec.this.encode(wrapper, value);
}
};
}
default <Z> NbtCodec<Z> apply(Function<T, Z> forward, Function<Z, T> back) {
return new NbtCodec<Z>() {
@Override
@@ -37,6 +37,7 @@ import com.github.retrooper.packetevents.protocol.nbt.NBTString;
import com.github.retrooper.packetevents.protocol.nbt.NBTType;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.resources.ResourceLocation;
import com.github.retrooper.packetevents.util.Either;
import com.github.retrooper.packetevents.util.UniqueIdUtil;
import com.github.retrooper.packetevents.util.mappings.IRegistry;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
@@ -336,20 +337,53 @@ public final class NbtCodecs {
@Override
public T decode(NBT nbt, PacketWrapper<?> wrapper) {
IRegistry<T> replacedRegistry = wrapper.replaceRegistry(registry);
T entry = null;
if (nbt instanceof NBTNumber) {
ClientVersion version = wrapper.getServerVersion().toClientVersion();
int id = ((NBTNumber) nbt).getAsInt();
return replacedRegistry.getByIdOrThrow(version, id);
entry = replacedRegistry.getById(version, id);
} else if (nbt instanceof NBTString) {
return replacedRegistry.getByNameOrThrow(((NBTString) nbt).getValue());
entry = replacedRegistry.getByName(((NBTString) nbt).getValue());
}
throw new IllegalStateException("Can't decode " + nbt + " for " + registry);
if (entry == null) {
throw new NbtCodecException("Can't decode registry " + registry.getRegistryKey());
}
return entry;
}
@Override
public NBT encode(PacketWrapper<?> wrapper, T value) {
if (!value.isRegistered()) {
throw new NbtCodecException("Unregistered entry");
}
return ResourceLocation.CODEC.encode(wrapper, value.getName());
}
};
}
public static <L, R> NbtCodec<Either<L, R>> either(NbtCodec<L> left, NbtCodec<R> right) {
return new NbtCodec<Either<L, R>>() {
@Override
public Either<L, R> decode(NBT nbt, PacketWrapper<?> wrapper) throws NbtCodecException {
try {
return Either.createLeft(left.decode(nbt, wrapper));
} catch (NbtCodecException leftException) {
try {
return Either.createRight(right.decode(nbt, wrapper));
} catch (NbtCodecException rightException) {
rightException.addSuppressed(leftException);
throw rightException;
}
}
}
@Override
public NBT encode(PacketWrapper<?> wrapper, Either<L, R> value) throws NbtCodecException {
return value.map(
v -> left.encode(wrapper, v),
v -> right.encode(wrapper, v)
);
}
};
}
}
@@ -25,7 +25,7 @@ import com.github.retrooper.packetevents.protocol.util.NbtCodecs;
import org.jspecify.annotations.NullMarked;
/**
* @version 1.21.11+
* @versions 1.21.11+
*/
@NullMarked
public interface AlphaColorModifier<A> extends AttributeModifier<AlphaColor, A> {
@@ -33,7 +33,7 @@ import java.util.List;
import java.util.Objects;
/**
* @version 1.21.11+
* @versions 1.21.11+
*/
@NullMarked
public class AmbientSounds {
@@ -49,8 +49,8 @@ public interface AttributeModifier<T, A> {
);
@SuppressWarnings("unchecked") // types don't matter
static <T> AttributeModifier<T, ?> override() {
return (AttributeModifier<T, ?>) OverrideModifier.INSTANCE;
static <T> AttributeModifier<T, T> override() {
return (AttributeModifier<T, T>) OverrideModifier.INSTANCE;
}
T apply(T value, A arg);
@@ -23,7 +23,7 @@ import com.github.retrooper.packetevents.protocol.util.NbtCodec;
import org.jspecify.annotations.NullMarked;
/**
* @version 1.21.11+
* @versions 1.21.11+
*/
@NullMarked
public interface AttributeType<T> extends MappedEntity {
@@ -37,7 +37,7 @@ import java.util.List;
import java.util.Map;
/**
* @version 1.21.11+
* @versions 1.21.11+
*/
@NullMarked
public final class AttributeTypes {
@@ -33,7 +33,7 @@ import java.util.List;
import java.util.Objects;
/**
* @version 1.21.11+
* @versions 1.21.11+
*/
@NullMarked
public class BackgroundMusic {
@@ -29,7 +29,7 @@ import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
/**
* @version 1.21.11+
* @versions 1.21.11+
*/
@NullMarked
public final class BlendToGrayArgument {
@@ -25,7 +25,7 @@ import com.github.retrooper.packetevents.protocol.util.NbtCodecs;
import org.jspecify.annotations.NullMarked;
/**
* @version 1.21.11+
* @versions 1.21.11+
*/
@NullMarked
public interface ColorModifier<A> extends AttributeModifier<Color, A> {
@@ -0,0 +1,61 @@
package com.github.retrooper.packetevents.protocol.world.attributes;
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.util.NbtCodec;
import com.github.retrooper.packetevents.protocol.util.NbtCodecException;
import com.github.retrooper.packetevents.protocol.util.NbtMapCodec;
import com.github.retrooper.packetevents.util.MathUtil;
import com.github.retrooper.packetevents.util.easing.CubicBezierControls;
import com.github.retrooper.packetevents.util.easing.CubicCurve;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
/**
* @versions 1.21.11+
*/
@NullMarked
public final class CubicBezierEasingType extends AbstractMappedEntity implements EasingType {
public static final NbtCodec<CubicBezierEasingType> CODEC = new NbtMapCodec<CubicBezierEasingType>() {
@Override
public CubicBezierEasingType decode(NBTCompound compound, PacketWrapper<?> wrapper) throws NbtCodecException {
CubicBezierControls controls = compound.getOrThrow("cubic_bezier", CubicBezierControls.CODEC, wrapper);
return new CubicBezierEasingType(controls);
}
@Override
public void encode(NBTCompound compound, PacketWrapper<?> wrapper, CubicBezierEasingType value) throws NbtCodecException {
compound.set("cubic_bezier", value.controls, CubicBezierControls.CODEC, wrapper);
}
}.codec();
private static final int NEWTON_RAPHSON_ITERATIONS = 4;
private final CubicBezierControls controls;
private final CubicCurve curveX;
private final CubicCurve curveY;
public CubicBezierEasingType(CubicBezierControls controls) {
super(null);
this.controls = controls;
this.curveX = controls.calcCurveX();
this.curveY = controls.calcCurveY();
}
@Override
public float apply(float x) {
// see https://en.wikipedia.org/w/index.php?title=Newton%27s_method&oldid=1321820598
// implementation is based on Mojang's code
float t = x;
for (int i = 0; i < NEWTON_RAPHSON_ITERATIONS; i++) {
float gradient = this.curveX.calcSampleGradient(t);
if (gradient < MathUtil.EPSILON) {
break;
}
float error = this.curveX.calcSample(t) - x;
t -= error / gradient;
}
return this.curveY.calcSample(t);
}
}
@@ -0,0 +1,22 @@
package com.github.retrooper.packetevents.protocol.world.attributes;
import com.github.retrooper.packetevents.protocol.mapper.MappedEntity;
import com.github.retrooper.packetevents.protocol.util.NbtCodec;
import com.github.retrooper.packetevents.protocol.util.NbtCodecs;
import com.github.retrooper.packetevents.util.Either;
import org.jspecify.annotations.NullMarked;
/**
* @versions 1.21.11+
*/
@NullMarked
public interface EasingType extends MappedEntity {
NbtCodec<EasingType> CODEC = NbtCodecs.either(
NbtCodecs.forRegistry(EasingTypes.getRegistry()),
CubicBezierEasingType.CODEC
).apply(Either::unwrap, type -> type instanceof CubicBezierEasingType
? Either.createRight((CubicBezierEasingType) type) : Either.createLeft(type));
float apply(float x);
}
@@ -0,0 +1,65 @@
package com.github.retrooper.packetevents.protocol.world.attributes;
import com.github.retrooper.packetevents.util.easing.EasingFunctions;
import com.github.retrooper.packetevents.util.FloatUnaryOperator;
import com.github.retrooper.packetevents.util.mappings.VersionedRegistry;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
/**
* @versions 1.21.1+
*/
@NullMarked
public final class EasingTypes {
private static final VersionedRegistry<EasingType> REGISTRY = new VersionedRegistry<>("easing_type");
private EasingTypes() {
}
@ApiStatus.Internal
public static EasingType define(String name, FloatUnaryOperator operator) {
return REGISTRY.define(name, data -> new StaticEasingType(data, operator));
}
public static VersionedRegistry<EasingType> getRegistry() {
return REGISTRY;
}
public static final EasingType CONSTANT = define("constant", EasingFunctions::constant);
public static final EasingType LINEAR = define("linear", EasingFunctions::linear);
public static final EasingType IN_BACK = define("in_back", EasingFunctions::inBack);
public static final EasingType IN_BOUNCE = define("in_bounce", EasingFunctions::inBounce);
public static final EasingType IN_CIRC = define("in_circ", EasingFunctions::inCirc);
public static final EasingType IN_CUBIC = define("in_cubic", EasingFunctions::inCubic);
public static final EasingType IN_ELASTIC = define("in_elastic", EasingFunctions::inElastic);
public static final EasingType IN_EXPO = define("in_expo", EasingFunctions::inExpo);
public static final EasingType IN_QUAD = define("in_quad", EasingFunctions::inQuad);
public static final EasingType IN_QUART = define("in_quart", EasingFunctions::inQuart);
public static final EasingType IN_QUINT = define("in_quint", EasingFunctions::inQuint);
public static final EasingType IN_SINE = define("in_sine", EasingFunctions::inSine);
public static final EasingType IN_OUT_BACK = define("in_out_back", EasingFunctions::inOutBack);
public static final EasingType IN_OUT_BOUNCE = define("in_out_bounce", EasingFunctions::inOutBounce);
public static final EasingType IN_OUT_CIRC = define("in_out_circ", EasingFunctions::inOutCirc);
public static final EasingType IN_OUT_CUBIC = define("in_out_cubic", EasingFunctions::inOutCubic);
public static final EasingType IN_OUT_ELASTIC = define("in_out_elastic", EasingFunctions::inOutElastic);
public static final EasingType IN_OUT_EXPO = define("in_out_expo", EasingFunctions::inOutExpo);
public static final EasingType IN_OUT_QUAD = define("in_out_quad", EasingFunctions::inOutQuad);
public static final EasingType IN_OUT_QUART = define("in_out_quart", EasingFunctions::inOutQuart);
public static final EasingType IN_OUT_QUINT = define("in_out_quint", EasingFunctions::inOutQuint);
public static final EasingType IN_OUT_SINE = define("in_out_sine", EasingFunctions::inOutSine);
public static final EasingType OUT_BACK = define("out_back", EasingFunctions::outBack);
public static final EasingType OUT_BOUNCE = define("out_bounce", EasingFunctions::outBounce);
public static final EasingType OUT_CIRC = define("out_circ", EasingFunctions::outCirc);
public static final EasingType OUT_CUBIC = define("out_cubic", EasingFunctions::outCubic);
public static final EasingType OUT_ELASTIC = define("out_elastic", EasingFunctions::outElastic);
public static final EasingType OUT_EXPO = define("out_expo", EasingFunctions::outExpo);
public static final EasingType OUT_QUAD = define("out_quad", EasingFunctions::outQuad);
public static final EasingType OUT_QUART = define("out_quart", EasingFunctions::outQuart);
public static final EasingType OUT_QUINT = define("out_quint", EasingFunctions::outQuint);
public static final EasingType OUT_SINE = define("out_sine", EasingFunctions::outSine);
static {
REGISTRY.unloadMappings();
}
}
@@ -23,7 +23,7 @@ import com.github.retrooper.packetevents.protocol.util.NbtCodec;
import org.jspecify.annotations.NullMarked;
/**
* @version 1.21.11+
* @versions 1.21.11+
*/
@NullMarked
public interface EnvironmentAttribute<T> extends MappedEntity {
@@ -22,6 +22,9 @@ import com.github.retrooper.packetevents.protocol.nbt.NBT;
import com.github.retrooper.packetevents.protocol.nbt.NBTCompound;
import com.github.retrooper.packetevents.protocol.util.NbtCodec;
import com.github.retrooper.packetevents.protocol.util.NbtCodecException;
import com.github.retrooper.packetevents.protocol.util.NbtCodecs;
import com.github.retrooper.packetevents.protocol.util.NbtMapCodec;
import com.github.retrooper.packetevents.util.Either;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
@@ -31,9 +34,10 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
/**
* @version 1.21.11++
* @versions 1.21.11++
*/
@NullMarked
public class EnvironmentAttributeMap {
@@ -134,41 +138,50 @@ public class EnvironmentAttributeMap {
this.modifier = modifier;
}
public static <T> Entry<T, T> createOverride(T value) {
return new Entry<>(value, AttributeModifier.override());
}
public static <T> NbtCodec<Entry<T, ?>> codec(EnvironmentAttribute<T> attribute) {
NbtCodec<T> valueCodec = attribute.getType().getValueCodec();
NbtCodec<AttributeModifier<T, ?>> modifierCodec = attribute.getType().getModifierCodec();
return new NbtCodec<Entry<T, ?>>() {
@Override
public Entry<T, ?> decode(NBT nbt, PacketWrapper<?> wrapper) throws NbtCodecException {
try {
return new Entry<>(valueCodec.decode(nbt, wrapper), AttributeModifier.override());
} catch (NbtCodecException ignored) {
}
NBTCompound compound = nbt.castOrThrow(NBTCompound.class);
AttributeModifier<T, ?> modifier = compound.getOrThrow("modifier", modifierCodec, wrapper);
Object arg = compound.getOrThrow("argument", modifier.argumentCodec(attribute), wrapper);
@SuppressWarnings("unchecked")
Entry<T, Object> entry = new Entry<>(arg, (AttributeModifier<T, ? super Object>) modifier);
return entry;
}
return NbtCodecs.either(
valueCodec,
new NbtMapCodec<Entry<T, ?>>() {
@Override
public Entry<T, ?> decode(NBTCompound compound, PacketWrapper<?> wrapper) throws NbtCodecException {
AttributeModifier<T, ?> modifier = compound.getOrThrow("modifier", modifierCodec, wrapper);
Object arg = compound.getOrThrow("argument", modifier.argumentCodec(attribute), wrapper);
@SuppressWarnings("unchecked")
Entry<T, Object> entry = new Entry<>(arg, (AttributeModifier<T, ? super Object>) modifier);
return entry;
}
@Override
public NBT encode(PacketWrapper<?> wrapper, Entry<T, ?> value) throws NbtCodecException {
return this.encode0(wrapper, value);
}
@Override
public void encode(NBTCompound compound, PacketWrapper<?> wrapper, Entry<T, ?> value) throws NbtCodecException {
this.encode0(compound, wrapper, value);
}
private <A> NBT encode0(PacketWrapper<?> wrapper, Entry<T, A> value) throws NbtCodecException {
if (value.modifier == AttributeModifier.override()) {
@SuppressWarnings("unchecked")
T valueArg = (T) value.argument;
return valueCodec.encode(wrapper, valueArg);
private <A> void encode0(NBTCompound compound, PacketWrapper<?> wrapper, Entry<T, A> value) throws NbtCodecException {
compound.set("modifier", value.modifier, modifierCodec, wrapper);
compound.set("argument", value.argument, value.modifier.argumentCodec(attribute), wrapper);
}
}.codec()
).apply(
e -> e.map(Entry::createOverride, Function.identity()),
e -> {
if (e.isOverride()) {
@SuppressWarnings("unchecked")
T arg = (T) e.argument;
return Either.createLeft(arg);
}
return Either.createRight(e);
}
NBTCompound compound = new NBTCompound();
compound.set("modifier", value.modifier, modifierCodec, wrapper);
compound.set("argument", value.argument, value.modifier.argumentCodec(attribute), wrapper);
return compound;
}
};
);
}
public boolean isOverride() {
return this.modifier == AttributeModifier.override();
}
public T getValue(T base) {
@@ -34,7 +34,7 @@ import java.util.Collections;
import java.util.List;
/**
* @version 1.21.11+
* @versions 1.21.11+
*/
@NullMarked
public final class EnvironmentAttributes {
@@ -25,7 +25,7 @@ import com.github.retrooper.packetevents.util.MathUtil;
import org.jspecify.annotations.NullMarked;
/**
* @version 1.21.11+
* @versions 1.21.11+
*/
@NullMarked
public interface FloatModifier<A> extends AttributeModifier<Float, A> {
@@ -29,14 +29,14 @@ public class KeyframeTrack<T> {
@Override
public KeyframeTrack<T> decode(NBTCompound compound, PacketWrapper<?> wrapper) throws NbtCodecException {
List<Keyframe<T>> keyframes = compound.getOrThrow("keyframes", keyframesCodec, wrapper);
EasingType easingType = compound.getOr("ease", EasingType.CODEC, EasingType.LINEAR, wrapper);
EasingType easingType = compound.getOr("ease", EasingType.CODEC, EasingTypes.LINEAR, wrapper);
return new KeyframeTrack<>(keyframes, easingType);
}
@Override
public void encode(NBTCompound compound, PacketWrapper<?> wrapper, KeyframeTrack<T> value) throws NbtCodecException {
compound.set("keyframes", value.keyframes, keyframesCodec, wrapper);
if (value.easingType != EasingType.LINEAR) {
if (value.easingType != EasingTypes.LINEAR) {
compound.set("ease", value.easingType, EasingType.CODEC, wrapper);
}
}
@@ -24,7 +24,7 @@ import com.github.retrooper.packetevents.protocol.util.NbtCodecs;
import org.jspecify.annotations.NullMarked;
/**
* @version 1.21.11+
* @versions 1.21.11+
*/
@NullMarked
public enum MoonPhase implements CodecNameable {
@@ -25,7 +25,7 @@ import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* @version 1.21.11+
* @versions 1.21.11+
*/
@NullMarked
public class StaticAttributeType<T> extends AbstractMappedEntity implements AttributeType<T> {
@@ -0,0 +1,31 @@
package com.github.retrooper.packetevents.protocol.world.attributes;
// Created by booky10 in packetevents (8:41 PM 07.12.2025)
import com.github.retrooper.packetevents.protocol.mapper.AbstractMappedEntity;
import com.github.retrooper.packetevents.util.FloatUnaryOperator;
import com.github.retrooper.packetevents.util.easing.CubicBezierControls;
import com.github.retrooper.packetevents.util.easing.CubicCurve;
import com.github.retrooper.packetevents.util.mappings.TypesBuilderData;
import org.jetbrains.annotations.ApiStatus;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* @versions 1.21.1+
*/
@NullMarked
public class StaticEasingType extends AbstractMappedEntity implements EasingType {
private final FloatUnaryOperator operator;
@ApiStatus.Internal
public StaticEasingType(@Nullable TypesBuilderData data, FloatUnaryOperator operator) {
super(data);
this.operator = operator;
}
@Override
public float apply(float x) {
return this.operator.apply(x);
}
}
@@ -25,7 +25,7 @@ import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;
/**
* @version 1.21.11+
* @versions 1.21.11+
*/
@NullMarked
public class StaticEnvironmentAttribute<T> extends AbstractMappedEntity implements EnvironmentAttribute<T> {
@@ -21,6 +21,7 @@ package com.github.retrooper.packetevents.util;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.function.Function;
public class Either<L, R> {
@@ -40,10 +41,18 @@ public class Either<L, R> {
return new Either<>(null, right);
}
public static <T> T unwrap(Either<T, ? extends T> either) {
return either.left != null ? either.left : either.right;
}
public Object get() {
return this.left != null ? this.left : this.right;
}
public <T> T map(Function<L, T> leftFn, Function<R, T> rightFn) {
return this.left != null ? leftFn.apply(this.left) : rightFn.apply(this.right);
}
public boolean isLeft() {
return this.left != null;
}
@@ -0,0 +1,10 @@
package com.github.retrooper.packetevents.util;
import org.jspecify.annotations.NullMarked;
@FunctionalInterface
@NullMarked
public interface FloatUnaryOperator {
float apply(float v);
}
@@ -20,9 +20,20 @@ package com.github.retrooper.packetevents.util;
public final class MathUtil {
public static final float EPSILON = 1e-5f;
public static final float F_PI = (float) Math.PI;
private MathUtil() {
}
public static float square(float x) {
return x * x;
}
public static float cube(float x) {
return x * x * x;
}
public static float lerp(float t, float a, float b) {
return t * (b - a) + a;
}
@@ -0,0 +1,68 @@
package com.github.retrooper.packetevents.util.easing;
import com.github.retrooper.packetevents.protocol.util.NbtCodec;
import com.github.retrooper.packetevents.protocol.util.NbtCodecs;
import org.jspecify.annotations.NullMarked;
import java.util.Arrays;
@NullMarked
public final class CubicBezierControls {
public static final NbtCodec<CubicBezierControls> CODEC = NbtCodecs.FLOAT.applyList()
.validate(floats -> floats.size() == 4)
.apply(l -> new CubicBezierControls(l.get(0), l.get(1), l.get(2), l.get(3)),
v -> Arrays.asList(v.x1, v.y1, v.x2, v.y2))
.validate(CubicBezierControls::isValid);
private final float x1;
private final float y1;
private final float x2;
private final float y2;
public CubicBezierControls(float x1, float y1, float x2, float y2) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
public boolean isValid() {
return this.x1 >= 0f && this.x1 <= 1f
&& this.x2 >= 0f && this.x2 <= 1f;
}
public CubicCurve calcCurveX() {
// based on Mojang's CubicBezier#curveFromControls
return new CubicCurve(
3f * this.x1 - 3f * this.x2 + 1f,
-6f * this.x1 + 3f * this.x2,
3f * this.x1
);
}
public CubicCurve calcCurveY() {
// based on Mojang's CubicBezier#curveFromControls
return new CubicCurve(
3f * this.y1 - 3f * this.y2 + 1f,
-6f * this.y1 + 3f * this.y2,
3f * this.y1
);
}
public float getX1() {
return this.x1;
}
public float getY1() {
return this.y1;
}
public float getX2() {
return this.x2;
}
public float getY2() {
return this.y2;
}
}
@@ -0,0 +1,38 @@
package com.github.retrooper.packetevents.util.easing;
import org.jspecify.annotations.NullMarked;
// based on Mojang's CubicCurve
@NullMarked
public final class CubicCurve {
private final float a;
private final float b;
private final float c;
public CubicCurve(float a, float b, float c) {
this.a = a;
this.b = b;
this.c = c;
}
public float calcSample(float t) {
return ((this.a * t + this.b) * t + this.c) * t;
}
public float calcSampleGradient(float t) {
return (3f * this.a * t + 2f * this.b) * t + this.c;
}
public float getA() {
return this.a;
}
public float getB() {
return this.b;
}
public float getC() {
return this.c;
}
}
@@ -0,0 +1,200 @@
package com.github.retrooper.packetevents.util.easing;
import com.github.retrooper.packetevents.util.MathUtil;
import org.jspecify.annotations.NullMarked;
// mostly copied from Mojang's Ease class, who took it from https://robertpenner.com/easing/
@NullMarked
public final class EasingFunctions {
private EasingFunctions() {
}
public static float constant(float ignoredX) {
return 0f;
}
public static float linear(float x) {
return x;
}
public static float inBack(float x) {
float c1 = 1.70158F;
float c3 = c1 + 1f;
return (x * x) * (c3 * x - c1);
}
public static float inBounce(float x) {
return 1f - outBounce(1f - x);
}
public static float inCubic(float x) {
return x * x * x;
}
public static float inElastic(float x) {
if (x == 0f) {
return 0f;
} else if (x == 1f) {
return 1f;
}
float c4 = (float) (Math.PI * 2d / 3d);
return (float) (-Math.pow(2d, 10d * (double) x - 10d) * Math.sin(((double) x * 10d - 10.75d) * c4));
}
public static float inExpo(float x) {
return x == 0f ? 0f : (float) Math.pow(2d, 10d * (double) x - 10d);
}
public static float inQuart(float x) {
return (x * x) * (x * x);
}
public static float inQuint(float x) {
return ((x * x) * (x * x)) * x;
}
public static float inSine(float x) {
return 1f - (float) Math.cos(x * (float) (Math.PI / 2));
}
public static float inOutBounce(float x) {
return x < 0.5f
? (1f - outBounce(1f - 2f * x)) / 2f
: (1f + outBounce(2f * x - 1f)) / 2f;
}
public static float inOutCirc(float x) {
return x < 0.5f
? (float) ((1d - Math.sqrt(1d - Math.pow(2d * (double) x, 2d))) / 2d)
: (float) ((Math.sqrt(1d - Math.pow(-2d * (double) x + 2d, 2d)) + 1d) / 2d);
}
public static float inOutCubic(float x) {
return x < 0.5f
? 4f * (x * x * x)
: (float) (1d - Math.pow(-2d * (double) x + 2d, 3d) / 2d);
}
public static float inOutQuad(float x) {
return x < 0.5f
? 2f * (x * x)
: (float) (1d - Math.pow(-2d * (double) x + 2d, 2d) / 2d);
}
public static float inOutQuart(float x) {
return x < 0.5f
? 8f * ((x * x) * (x * x))
: (float) (1d - Math.pow(-2d * (double) x + 2d, 4d) / 2d);
}
public static float inOutQuint(float x) {
return (double) x < 0.5d
? 16f * x * x * x * x * x
: (float) (1d - Math.pow(-2d * (double) x + 2d, 5d) / 2d);
}
public static float outBounce(float x) {
float n1 = 7.5625f;
float d1 = 2.75f;
if (x < 1f / d1) {
return n1 * (x * x);
} else if (x < 2f / d1) {
return n1 * MathUtil.square(x - (1.5f / d1)) + (3f / 4f);
} else if ((double) x < 2.5f / d1) {
return n1 * MathUtil.square(x - (2.25f / d1)) + (15f / 16f);
}
return n1 * MathUtil.square(x - (2.625f / d1)) + (63f / 64f);
}
public static float outElastic(float x) {
float c4 = (float) (Math.PI * 2d / 3d);
if (x == 0f) {
return 0f;
} else if (x == 1f) {
return 1f;
}
return (float) (Math.pow(2d, -10d * (double) x) * Math.sin(((double) x * 10d - 0.75d) * c4) + 1d);
}
public static float outExpo(float x) {
return x == 1f ? 1f : 1f - (float) Math.pow(2d, -10d * (double) x);
}
public static float outQuad(float x) {
return 1f - MathUtil.square(1f - x);
}
public static float outQuint(float x) {
return 1f - (float) Math.pow(1d - (double) x, 5d);
}
public static float outSine(float x) {
return (float) Math.sin(x * (float) (Math.PI / 2));
}
public static float inOutSine(float x) {
return -((float) Math.cos((float) Math.PI * x) - 1f) / 2f;
}
public static float outBack(float x) {
float c1 = 1.70158F;
float c3 = 2.70158F;
return 1f + c3 * MathUtil.cube(x - 1f) + c1 * MathUtil.square(x - 1f);
}
public static float outQuart(float x) {
return 1f - MathUtil.square(MathUtil.square(1f - x));
}
public static float outCubic(float x) {
return 1f - MathUtil.cube(1f - x);
}
public static float inOutExpo(float x) {
if (x == 0f) {
return 0f;
} else if (x == 1f) {
return 1f;
}
return x < 0.5f
? (float) (Math.pow(2d, 20d * (double) x - 10d) / 2d)
: (float) ((2d - Math.pow(2d, -20d * (double) x + 10d)) / 2d);
}
public static float inQuad(float x) {
return x * x;
}
public static float outCirc(float x) {
return (float) Math.sqrt(1f - MathUtil.square(x - 1f));
}
public static float inOutElastic(float x) {
float c5 = (float) Math.PI * 4.0F / 9.0F;
if (x == 0f) {
return 0f;
} else if (x == 1f) {
return 1f;
}
double sin = Math.sin((20d * (double) x - 11.125d) * c5);
return x < 0.5f
? (float) (-(Math.pow(2d, 20d * (double) x - 10d) * sin) / 2d)
: (float) (Math.pow(2d, -20d * (double) x + 10d) * sin / 2d + 1d);
}
public static float inCirc(float x) {
return (float) (-Math.sqrt(1f - x * x)) + 1f;
}
public static float inOutBack(float x) {
float c1 = 1.70158f;
float c2 = c1 * 1.525f;
if (x < 0.5F) {
return 2f * 2f * x * x * (2f * (c2 + 1f) * x - c2) / 2f;
} else {
float dt = 2f * x - 2f;
return (dt * dt * ((c2 + 1f) * dt + c2) + 2f) / 2f;
}
}
}
+36
View File
@@ -0,0 +1,36 @@
{
"V_1_21_11": [
"constant",
"linear",
"in_back",
"in_bounce",
"in_circ",
"in_cubic",
"in_elastic",
"in_expo",
"in_quad",
"in_quart",
"in_quint",
"in_sine",
"in_out_back",
"in_out_bounce",
"in_out_circ",
"in_out_cubic",
"in_out_elastic",
"in_out_expo",
"in_out_quad",
"in_out_quart",
"in_out_quint",
"in_out_sine",
"out_back",
"out_bounce",
"out_circ",
"out_cubic",
"out_elastic",
"out_expo",
"out_quad",
"out_quart",
"out_quint",
"out_sine"
]
}