Remove build artifacts, dev tool configs, unused dependencies, and third-party source dumps. Add proper README, update .gitignore, clean up Makefile.
157 lines
5.3 KiB
Java
157 lines
5.3 KiB
Java
package com.tiedup.remake.client.animation;
|
|
|
|
import com.mojang.logging.LogUtils;
|
|
import dev.kosmx.playerAnim.api.layered.IAnimation;
|
|
import dev.kosmx.playerAnim.api.layered.KeyframeAnimationPlayer;
|
|
import dev.kosmx.playerAnim.api.layered.ModifierLayer;
|
|
import dev.kosmx.playerAnim.core.data.KeyframeAnimation;
|
|
import dev.kosmx.playerAnim.minecraftApi.PlayerAnimationRegistry;
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
import java.util.UUID;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
import net.minecraft.client.multiplayer.ClientLevel;
|
|
import net.minecraft.client.player.AbstractClientPlayer;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraftforge.api.distmarker.Dist;
|
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
|
import org.slf4j.Logger;
|
|
|
|
/**
|
|
* Manages pending animations for remote players whose animation layers
|
|
* may not be immediately available due to timing issues.
|
|
*
|
|
* <p>When a player is tied, the sync packet may arrive before the remote player's
|
|
* animation layer is initialized by PlayerAnimator. This class queues failed
|
|
* animation attempts and retries them each tick until success or timeout.
|
|
*
|
|
* <p>This follows the same pattern as SyncManager's pending queue for inventory sync.
|
|
*/
|
|
@OnlyIn(Dist.CLIENT)
|
|
public class PendingAnimationManager {
|
|
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
|
|
/** Pending animations waiting for layer initialization */
|
|
private static final Map<UUID, PendingEntry> pending =
|
|
new ConcurrentHashMap<>();
|
|
|
|
/** Maximum retry attempts before giving up (~2 seconds at 20 ticks/sec) */
|
|
private static final int MAX_RETRIES = 40;
|
|
|
|
/**
|
|
* Queue a player's animation for retry.
|
|
* Called when playAnimation fails due to null layer.
|
|
*
|
|
* @param uuid The player's UUID
|
|
* @param animId The animation ID (without namespace)
|
|
*/
|
|
public static void queueForRetry(UUID uuid, String animId) {
|
|
pending.compute(uuid, (k, existing) -> {
|
|
if (existing == null) {
|
|
LOGGER.debug(
|
|
"Queued animation '{}' for retry on player {}",
|
|
animId,
|
|
uuid
|
|
);
|
|
return new PendingEntry(animId, 0);
|
|
}
|
|
// Update animation ID but preserve retry count
|
|
return new PendingEntry(animId, existing.retries);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Remove a player from the pending queue.
|
|
* Called when animation succeeds or player disconnects.
|
|
*
|
|
* @param uuid The player's UUID
|
|
*/
|
|
public static void remove(UUID uuid) {
|
|
pending.remove(uuid);
|
|
}
|
|
|
|
/**
|
|
* Check if a player has a pending animation.
|
|
*
|
|
* @param uuid The player's UUID
|
|
* @return true if pending
|
|
*/
|
|
public static boolean hasPending(UUID uuid) {
|
|
return pending.containsKey(uuid);
|
|
}
|
|
|
|
/**
|
|
* Process pending animations. Called every tick from AnimationTickHandler.
|
|
* Attempts to play queued animations and removes successful or expired entries.
|
|
*
|
|
* @param level The client level
|
|
*/
|
|
public static void processPending(ClientLevel level) {
|
|
if (pending.isEmpty()) return;
|
|
|
|
Iterator<Map.Entry<UUID, PendingEntry>> it = pending
|
|
.entrySet()
|
|
.iterator();
|
|
|
|
while (it.hasNext()) {
|
|
Map.Entry<UUID, PendingEntry> entry = it.next();
|
|
UUID uuid = entry.getKey();
|
|
PendingEntry pe = entry.getValue();
|
|
|
|
// Check expiration
|
|
if (pe.retries >= MAX_RETRIES) {
|
|
LOGGER.warn("Animation retry exhausted for player {}", uuid);
|
|
it.remove();
|
|
continue;
|
|
}
|
|
|
|
// Try to find player and play animation
|
|
Player player = level.getPlayerByUUID(uuid);
|
|
if (player instanceof AbstractClientPlayer clientPlayer) {
|
|
ModifierLayer<IAnimation> layer =
|
|
BondageAnimationManager.getPlayerLayerSafe(clientPlayer);
|
|
|
|
if (layer != null) {
|
|
ResourceLocation loc =
|
|
ResourceLocation.fromNamespaceAndPath(
|
|
"tiedup",
|
|
pe.animId
|
|
);
|
|
KeyframeAnimation anim =
|
|
PlayerAnimationRegistry.getAnimation(loc);
|
|
|
|
if (anim != null) {
|
|
layer.setAnimation(new KeyframeAnimationPlayer(anim));
|
|
LOGGER.info(
|
|
"Animation retry succeeded for {} after {} attempts",
|
|
clientPlayer.getName().getString(),
|
|
pe.retries
|
|
);
|
|
it.remove();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Increment retry count
|
|
pending.put(uuid, new PendingEntry(pe.animId, pe.retries + 1));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear all pending animations.
|
|
* Called on world unload.
|
|
*/
|
|
public static void clearAll() {
|
|
pending.clear();
|
|
LOGGER.debug("Cleared all pending animations");
|
|
}
|
|
|
|
/**
|
|
* Record to store pending animation data.
|
|
*/
|
|
private record PendingEntry(String animId, int retries) {}
|
|
}
|