Fixed interpolators not being unique per entity/player

This commit is contained in:
Sollace 2019-01-06 18:07:42 +02:00
parent 6947c8e994
commit 90cd8c4cea
28 changed files with 147 additions and 65 deletions

View file

@ -25,6 +25,7 @@ import net.minecraft.util.EnumHandSide;
import net.minecraft.util.math.MathHelper;
import java.util.Random;
import java.util.UUID;
import static net.minecraft.client.renderer.GlStateManager.*;
@ -63,6 +64,8 @@ public abstract class AbstractPonyModel extends ModelPlayer implements IModel, P
public IModelPart tail;
public PonySnout snout;
public UUID interpolatorId;
public AbstractPonyModel(boolean arms) {
super(0, arms);
}
@ -93,6 +96,7 @@ public abstract class AbstractPonyModel extends ModelPlayer implements IModel, P
isSwimming = pony.isSwimming(entity);
headGear = pony.isWearingHeadgear(entity);
isRidingInteractive = pony.isRidingInteractive(entity);
interpolatorId = entity.getUniqueID();
}
/**
@ -243,7 +247,7 @@ public abstract class AbstractPonyModel extends ModelPlayer implements IModel, P
* @param ticks Total whole and partial ticks since the entity's existance. Used in animations together with {@code swing} and {@code move}.
*/
protected void shakeBody(float move, float swing, float bodySwing, float ticks) {
tail.setRotationAndAngles(isSwimming() || rainboom, move, swing, bodySwing * 5, ticks);
tail.setRotationAndAngles(isSwimming() || rainboom, interpolatorId, move, swing, bodySwing * 5, ticks);
upperTorso.rotateAngleY = bodySwing;
bipedBody.rotateAngleY = bodySwing;
@ -308,7 +312,7 @@ public abstract class AbstractPonyModel extends ModelPlayer implements IModel, P
float legRPX = cos - getLegOutset() - 0.001F;
legRPX = metadata.getInterpolator().interpolate("legOffset", legRPX, 3);
legRPX = metadata.getInterpolator(entity.getUniqueID()).interpolate("legOffset", legRPX, 3);
bipedRightArm.rotationPointX = -legRPX;
bipedRightLeg.rotationPointX = -legRPX;
@ -881,7 +885,7 @@ public abstract class AbstractPonyModel extends ModelPlayer implements IModel, P
}
upperTorso.render(scale);
bipedBody.postRender(scale);
tail.renderPart(scale);
tail.renderPart(scale, entity.getUniqueID());
}
protected void renderLegs(float scale) {

View file

@ -1,9 +1,30 @@
package com.minelittlepony.model.anim;
import com.minelittlepony.util.chron.ChronicCache;
import com.minelittlepony.util.chron.Touchable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class BasicEasingInterpolator implements IInterpolator {
public class BasicEasingInterpolator extends Touchable<BasicEasingInterpolator> implements IInterpolator {
private static ChronicCache<UUID, BasicEasingInterpolator> instanceCache = new ChronicCache<>();
/**
* Gets or creates a new basic, linear interpolator for the provided id.
*/
public static IInterpolator getInstance(UUID id) {
return instanceCache.retrieve(id, BasicEasingInterpolator::new);
}
public BasicEasingInterpolator() {
}
private BasicEasingInterpolator(UUID id) {
}
private final Map<String, Float> properties = new HashMap<String, Float>();

View file

@ -1,5 +1,7 @@
package com.minelittlepony.model.capabilities;
import java.util.UUID;
public interface IModelPart {
/**
* Initialises all of the boxes in this modelpart.
@ -15,14 +17,14 @@ public interface IModelPart {
*
* See {@link AbstractPonyMode.setRotationAndAngle} for an explanation of the various parameters.
*/
default void setRotationAndAngles(boolean rainboom, float move, float swing, float bodySwing, float ticks) {
default void setRotationAndAngles(boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) {
}
/**
* Renders this model component.
*/
void renderPart(float scale);
void renderPart(float scale, UUID interpolatorId);
/**
* Sets whether this part should be rendered.

View file

@ -5,6 +5,8 @@ import net.minecraft.client.renderer.GlStateManager;
import com.minelittlepony.model.AbstractPonyModel;
import com.minelittlepony.model.capabilities.IModelPegasus;
import java.util.UUID;
public class BatWings<T extends AbstractPonyModel & IModelPegasus> extends PegasusWings<T> {
public BatWings(T model, float yOffset, float stretch) {
@ -23,12 +25,12 @@ public class BatWings<T extends AbstractPonyModel & IModelPegasus> extends Pegas
}
@Override
public void renderPart(float scale) {
public void renderPart(float scale, UUID interpolatorId) {
GlStateManager.pushMatrix();
GlStateManager.scale(1.3F, 1.3F, 1.3F);
super.renderPart(scale);
super.renderPart(scale, interpolatorId);
GlStateManager.popMatrix();
}

View file

@ -55,7 +55,7 @@ public class ModelPonyHead extends ModelHumanoidHead implements ICapitated {
if (metadata.hasMagic()) {
skeletonHead.postRender(scale);
horn.renderPart(scale);
horn.renderPart(scale, entity.getUniqueID());
}
}

View file

@ -9,6 +9,8 @@ import com.minelittlepony.model.capabilities.IModelPart;
import com.minelittlepony.model.capabilities.IModelPegasus;
import com.minelittlepony.pony.data.PonyWearable;
import java.util.UUID;
public class PegasusWings<T extends AbstractPonyModel & IModelPegasus> implements IModelPart {
protected final T pegasus;
@ -41,7 +43,7 @@ public class PegasusWings<T extends AbstractPonyModel & IModelPegasus> implement
}
@Override
public void setRotationAndAngles(boolean rainboom, float move, float swing, float bodySwing, float ticks) {
public void setRotationAndAngles(boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) {
float flap = 0;
float progress = pegasus.getSwingAmount();
@ -69,7 +71,7 @@ public class PegasusWings<T extends AbstractPonyModel & IModelPegasus> implement
}
if (!pegasus.isFlying()) {
flapAngle = pegasus.getMetadata().getInterpolator().interpolate("wingFlap", flapAngle, 10);
flapAngle = pegasus.getMetadata().getInterpolator(interpolatorId).interpolate("wingFlap", flapAngle, 10);
}
getLeft().rotateFlying(flapAngle);
@ -78,7 +80,7 @@ public class PegasusWings<T extends AbstractPonyModel & IModelPegasus> implement
}
@Override
public void renderPart(float scale) {
public void renderPart(float scale, UUID interpolatorId) {
getLeft().render(scale);
getRight().render(scale);
}

View file

@ -9,6 +9,8 @@ import com.minelittlepony.model.AbstractPonyModel;
import com.minelittlepony.model.capabilities.IModelPart;
import com.minelittlepony.render.model.PlaneRenderer;
import java.util.UUID;
public class PonyTail extends PlaneRenderer implements IModelPart {
private static final int SEGMENTS = 4;
@ -30,7 +32,7 @@ public class PonyTail extends PlaneRenderer implements IModelPart {
}
@Override
public void setRotationAndAngles(boolean rainboom, float move, float swing, float bodySwing, float ticks) {
public void setRotationAndAngles(boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) {
rotateAngleZ = rainboom ? 0 : MathHelper.cos(move * 0.8F) * 0.2f * swing;
rotateAngleY = bodySwing;
@ -76,7 +78,7 @@ public class PonyTail extends PlaneRenderer implements IModelPart {
}
@Override
public void renderPart(float scale) {
public void renderPart(float scale, UUID interpolatorId) {
render(scale);
}

View file

@ -6,6 +6,8 @@ import com.minelittlepony.model.capabilities.IModelPart;
import com.minelittlepony.render.model.PlaneRenderer;
import com.minelittlepony.render.model.PonyRenderer;
import java.util.UUID;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.util.math.MathHelper;
@ -52,7 +54,7 @@ public class SeaponyTail implements IModelPart {
}
@Override
public void setRotationAndAngles(boolean rainboom, float move, float swing, float bodySwing, float ticks) {
public void setRotationAndAngles(boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) {
float rotation = model.isSleeping() ? 0 : MathHelper.sin(ticks * 0.536f) / 4;
tailBase.rotateAngleX = TAIL_ROTX + rotation;
@ -61,7 +63,7 @@ public class SeaponyTail implements IModelPart {
}
@Override
public void renderPart(float scale) {
public void renderPart(float scale, UUID interpolatorId) {
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
GlStateManager.enableBlend();
tailBase.render(scale);

View file

@ -5,6 +5,8 @@ import com.minelittlepony.model.capabilities.IModelPart;
import com.minelittlepony.render.model.GlowRenderer;
import com.minelittlepony.render.model.PonyRenderer;
import java.util.UUID;
import net.minecraft.client.model.ModelBase;
import static org.lwjgl.opengl.GL11.*;
@ -38,7 +40,7 @@ public class UnicornHorn implements IModelPart {
}
@Override
public void renderPart(float scale) {
public void renderPart(float scale, UUID interpolatorId) {
if (isVisible) {
horn.render(scale);
}

View file

@ -39,7 +39,7 @@ public abstract class AbstractGear extends ModelBase implements IGear, PonyModel
TextureManager tex = Minecraft.getMinecraft().getRenderManager().renderEngine;
tex.bindTexture(getTexture(entity));
renderPart(scale);
renderPart(scale, entity.getUniqueID());
GL11.glPopAttrib();
}

View file

@ -13,6 +13,7 @@ import com.minelittlepony.render.model.PonyRenderer;
import com.minelittlepony.util.render.Color;
import java.util.Calendar;
import java.util.UUID;
public class ChristmasHat extends AbstractGear {
@ -57,7 +58,7 @@ public class ChristmasHat extends AbstractGear {
}
@Override
public void setRotationAndAngles(boolean rainboom, float move, float swing, float bodySwing, float ticks) {
public void setRotationAndAngles(boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) {
float pi = PI * (float) Math.pow(swing, 16);
float mve = move * 0.6662f;
@ -88,7 +89,7 @@ public class ChristmasHat extends AbstractGear {
}
@Override
public void renderPart(float scale) {
public void renderPart(float scale, UUID interpolatorId) {
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
if (tint != 0) {

View file

@ -8,6 +8,8 @@ import com.minelittlepony.model.capabilities.IModel;
import com.minelittlepony.pony.data.PonyWearable;
import com.minelittlepony.render.model.PonyRenderer;
import java.util.UUID;
public class Muffin extends AbstractGear implements IStackable {
private static final ResourceLocation TEXTURE = new ResourceLocation("minelittlepony", "textures/models/muffin.png");
@ -26,7 +28,7 @@ public class Muffin extends AbstractGear implements IStackable {
}
@Override
public void renderPart(float scale) {
public void renderPart(float scale, UUID interpolatorId) {
crown.render(scale);
}

View file

@ -6,6 +6,8 @@ import com.minelittlepony.model.capabilities.IModelPegasus;
import com.minelittlepony.pony.data.PonyWearable;
import com.minelittlepony.render.model.PlaneRenderer;
import java.util.UUID;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.entity.Entity;
import net.minecraft.util.ResourceLocation;
@ -82,7 +84,7 @@ public class SaddleBags extends AbstractGear {
}
@Override
public void setRotationAndAngles(boolean rainboom, float move, float swing, float bodySwing, float ticks) {
public void setRotationAndAngles(boolean rainboom, UUID interpolatorId, float move, float swing, float bodySwing, float ticks) {
float pi = PI * (float) Math.pow(swing, 16);
float mve = move * 0.6662f;
@ -109,8 +111,8 @@ public class SaddleBags extends AbstractGear {
}
@Override
public void renderPart(float scale) {
dropAmount = model.getMetadata().getInterpolator().interpolate("dropAmount", dropAmount, 3);
public void renderPart(float scale, UUID interpolatorId) {
dropAmount = model.getMetadata().getInterpolator(interpolatorId).interpolate("dropAmount", dropAmount, 3);
GlStateManager.pushMatrix();
GlStateManager.translate(0, dropAmount, 0);

View file

@ -9,6 +9,8 @@ import com.minelittlepony.pony.data.PonyWearable;
import com.minelittlepony.render.model.PlaneRenderer;
import com.minelittlepony.render.model.PonyRenderer;
import java.util.UUID;
public class Stetson extends AbstractGear implements IStackable {
private static final ResourceLocation TEXTURE = new ResourceLocation("minelittlepony", "textures/models/stetson.png");
@ -41,7 +43,7 @@ public class Stetson extends AbstractGear implements IStackable {
}
@Override
public void renderPart(float scale) {
public void renderPart(float scale, UUID interpolatorId) {
rimshot.render(scale);
}

View file

@ -8,6 +8,8 @@ import com.minelittlepony.model.capabilities.IModel;
import com.minelittlepony.pony.data.PonyWearable;
import com.minelittlepony.render.model.PonyRenderer;
import java.util.UUID;
public class WitchHat extends AbstractGear implements IStackable {
private static final ResourceLocation WITCH_TEXTURES = new ResourceLocation("textures/entity/witch.png");
@ -15,7 +17,7 @@ public class WitchHat extends AbstractGear implements IStackable {
private PonyRenderer witchHat;
@Override
public void renderPart(float scale) {
public void renderPart(float scale, UUID interpolatorId) {
witchHat.render(scale * 1.3F);
}

View file

@ -28,7 +28,7 @@ public class ModelAlicorn extends ModelUnicorn implements IModelPegasus {
super.setRotationAngles(move, swing, ticks, headYaw, headPitch, scale, entity);
if (canFly()) {
wings.setRotationAndAngles(rainboom, move, swing, 0, ticks);
wings.setRotationAndAngles(rainboom, entity.getUniqueID(), move, swing, 0, ticks);
}
}
@ -36,7 +36,7 @@ public class ModelAlicorn extends ModelUnicorn implements IModelPegasus {
protected void renderBody(Entity entity, float move, float swing, float ticks, float headYaw, float headPitch, float scale) {
super.renderBody(entity, move, swing, ticks, headYaw, headPitch, scale);
if (canFly()) {
wings.renderPart(scale);
wings.renderPart(scale, entity.getUniqueID());
}
}
}

View file

@ -26,12 +26,12 @@ public class ModelPegasus extends ModelEarthPony implements IModelPegasus {
@Override
public void setRotationAngles(float move, float swing, float ticks, float headYaw, float headPitch, float scale, Entity entity) {
super.setRotationAngles(move, swing, ticks, headYaw, headPitch, scale, entity);
wings.setRotationAndAngles(rainboom, move, swing, 0, ticks);
wings.setRotationAndAngles(rainboom, entity.getUniqueID(), move, swing, 0, ticks);
}
@Override
protected void renderBody(Entity entity, float move, float swing, float ticks, float headYaw, float headPitch, float scale) {
super.renderBody(entity, move, swing, ticks, headYaw, headPitch, scale);
wings.renderPart(scale);
wings.renderPart(scale, entity.getUniqueID());
}
}

View file

@ -138,7 +138,7 @@ public class ModelUnicorn extends ModelEarthPony implements IModelUnicorn {
super.renderHead(entity, move, swing, ticks, headYaw, headPitch, scale);
if (canCast()) {
horn.renderPart(scale);
horn.renderPart(scale, entity.getUniqueID());
if (isCasting()) {
horn.renderMagic(getMagicColor(), scale);
}

View file

@ -152,7 +152,7 @@ public class ModelSeapony extends ModelUnicorn {
bodyCenter.render(scale);
bipedBody.postRender(scale);
tail.renderPart(scale);
tail.renderPart(scale, entity.getUniqueID());
GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
GlStateManager.enableBlend();

View file

@ -2,6 +2,8 @@ package com.minelittlepony.pony.data;
import com.minelittlepony.model.anim.IInterpolator;
import java.util.UUID;
import net.minecraft.client.resources.data.IMetadataSection;
/**
@ -46,5 +48,5 @@ public interface IPonyData extends IMetadataSection {
/**
* Gets an interpolator for interpolating values.
*/
IInterpolator getInterpolator();
IInterpolator getInterpolator(UUID interpolatorId);
}

View file

@ -3,6 +3,7 @@ package com.minelittlepony.pony.data;
import com.google.common.base.MoreObjects;
import com.minelittlepony.MineLittlePony;
import com.minelittlepony.ducks.IRenderPony;
import com.minelittlepony.util.chron.Touchable;
import com.voxelmodpack.hdskins.resources.texture.DynamicTextureImage;
import com.voxelmodpack.hdskins.resources.texture.IBufferedTexture;
import com.voxelmodpack.hdskins.util.ProfileTextureUtil;
@ -33,7 +34,7 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@Immutable
public class Pony implements IPony {
public class Pony extends Touchable<Pony> implements IPony {
private static final AtomicInteger ponyCount = new AtomicInteger();
@ -42,22 +43,11 @@ public class Pony implements IPony {
private final ResourceLocation texture;
private final IPonyData metadata;
private long expirationPeriod;
public Pony(ResourceLocation resource) {
texture = resource;
metadata = checkSkin(texture);
}
boolean hasExpired() {
return expirationPeriod <= System.currentTimeMillis();
}
Pony touch() {
expirationPeriod = System.currentTimeMillis() + 30000;
return this;
}
private IPonyData checkSkin(ResourceLocation resource) {
IPonyData data = checkPonyMeta(resource);
if (data != null) {

View file

@ -5,6 +5,8 @@ import com.minelittlepony.model.anim.BasicEasingInterpolator;
import com.minelittlepony.model.anim.IInterpolator;
import java.awt.image.BufferedImage;
import java.util.UUID;
import javax.annotation.concurrent.Immutable;
@ -22,8 +24,6 @@ public class PonyData implements IPonyData {
private final boolean[] wearables;
private final IInterpolator interpolator = new BasicEasingInterpolator();
public PonyData() {
race = PonyRace.HUMAN;
tailSize = TailLengths.FULL;
@ -92,8 +92,8 @@ public class PonyData implements IPonyData {
}
@Override
public IInterpolator getInterpolator() {
return interpolator;
public IInterpolator getInterpolator(UUID interpolatorId) {
return BasicEasingInterpolator.getInstance(interpolatorId);
}
/**

View file

@ -1,11 +1,11 @@
package com.minelittlepony.pony.data;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.JsonParseException;
import com.minelittlepony.MineLittlePony;
import com.minelittlepony.PonyConfig;
import com.minelittlepony.util.chron.ChronicCache;
import com.minelittlepony.util.math.MathUtil;
import com.voxelmodpack.hdskins.ISkinCacheClearListener;
import com.voxelmodpack.hdskins.util.MoreStreams;
@ -25,7 +25,6 @@ import java.io.Reader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.UUID;
@ -49,7 +48,7 @@ public class PonyManager implements IResourceManagerReloadListener, ISkinCacheCl
private PonyConfig config;
private final Map<ResourceLocation, Pony> poniesCache = Maps.newHashMap();
private final ChronicCache<ResourceLocation, Pony> poniesCache = new ChronicCache<>();
public PonyManager(PonyConfig config) {
this.config = config;
@ -61,11 +60,7 @@ public class PonyManager implements IResourceManagerReloadListener, ISkinCacheCl
* @param resource A texture resource
*/
public IPony getPony(ResourceLocation resource) {
IPony result = poniesCache.computeIfAbsent(resource, Pony::new).touch();
poniesCache.entrySet().removeIf(entry -> entry.getValue().hasExpired());
return result;
return poniesCache.retrieve(resource, Pony::new);
}
/**
@ -78,7 +73,7 @@ public class PonyManager implements IResourceManagerReloadListener, ISkinCacheCl
ResourceLocation skin = player.getLocationSkin();
UUID uuid = player.getGameProfile().getId();
if (Pony.getBufferedImage(skin) == null) {
if (true || Pony.getBufferedImage(skin) == null) {
return getDefaultPony(uuid);
}
@ -89,7 +84,7 @@ public class PonyManager implements IResourceManagerReloadListener, ISkinCacheCl
ResourceLocation skin = playerInfo.getLocationSkin();
UUID uuid = playerInfo.getGameProfile().getId();
if (Pony.getBufferedImage(skin) == null) {
if (true || Pony.getBufferedImage(skin) == null) {
return getDefaultPony(uuid);
}
@ -137,7 +132,7 @@ public class PonyManager implements IResourceManagerReloadListener, ISkinCacheCl
* @param uuid A UUID. Either a user or an entity.
*/
public IPony getBackgroundPony(UUID uuid) {
if (getNumberOfPonies() == 0 || isUser(uuid)) {
if (true || getNumberOfPonies() == 0 || isUser(uuid)) {
return getPony(getDefaultSkin(uuid));
}
@ -229,7 +224,7 @@ public class PonyManager implements IResourceManagerReloadListener, ISkinCacheCl
* Returns true if the given uuid is of a player would would use the ALEX skin type.
*/
public static boolean isSlimSkin(UUID uuid) {
return (uuid.hashCode() & 1) == 1;
return true || (uuid.hashCode() & 1) == 1;
}
private int getNumberOfPonies() {

View file

@ -83,8 +83,8 @@ public class LayerGear<T extends EntityLivingBase> extends AbstractPonyLayer<T>
getRenderer().bindTexture(texture);
gear.setLivingAnimations(model, entity);
gear.setRotationAndAngles(model.isGoingFast(), move, swing, model.getWobbleAmount(), ticks);
gear.renderPart(scale);
gear.setRotationAndAngles(model.isGoingFast(), entity.getUniqueID(), move, swing, model.getWobbleAmount(), ticks);
gear.renderPart(scale, entity.getUniqueID());
GL11.glPopAttrib();
}

View file

@ -29,7 +29,7 @@ public class RenderSeaponyPlayer extends RenderPonyPlayer {
mainModel = renderPony.setPonyModel(wet ? seapony : normalPony);
float state = wet ? 100 : 0;
float interpolated = pony.getMetadata().getInterpolator().interpolate("seapony_state", state, 5);
float interpolated = pony.getMetadata().getInterpolator(player.getUniqueID()).interpolate("seapony_state", state, 5);
if (!MathUtil.compareFloats(interpolated, state)) {
double x = player.posX + (player.getEntityWorld().rand.nextFloat() * 2) - 1;

View file

@ -20,7 +20,7 @@ public class PostureFlight extends MotionCompositor implements PonyPosture<Abstr
float roll = (float)calculateRoll(player, motionX, motionY, motionZ);
roll = model.getMetadata().getInterpolator().interpolate("pegasusRoll", roll, 10);
roll = model.getMetadata().getInterpolator(player.getUniqueID()).interpolate("pegasusRoll", roll, 10);
GlStateManager.rotate((float)roll, 0, 0, 1);
}

View file

@ -0,0 +1,20 @@
package com.minelittlepony.util.chron;
import java.util.HashMap;
import java.util.function.Function;
/**
* Special version of a map that culls its own values.
*/
public class ChronicCache<K, V extends Touchable<V>> extends HashMap<K, V> {
private static final long serialVersionUID = 6454924015818181978L;
public V retrieve(K key, Function<? super K, ? extends V> mappingFunction) {
V result = computeIfAbsent(key, mappingFunction).touch();
entrySet().removeIf(entry -> entry.getValue().hasExpired());
return result;
}
}

View file

@ -0,0 +1,29 @@
package com.minelittlepony.util.chron;
/**
* DON'T TOUCH ME I'M SCAREED
*
* Basic touchable object that expires if not gently caressed for all of 30 seconds.
*/
public abstract class Touchable<T extends Touchable<T>> {
private long expirationPeriod;
/**
* Returns whether this object is dead (expired).
* Expired Touchables are flushed from the ChronicCache on next access.
*/
public boolean hasExpired() {
return expirationPeriod <= System.currentTimeMillis();
}
/**
* Touches this object.
* Internally just updates the expiration date.
*/
@SuppressWarnings("unchecked")
public T touch() {
expirationPeriod = System.currentTimeMillis() + 30000;
return (T)this;
}
}