-
Notifications
You must be signed in to change notification settings - Fork 6
/
GeoItem.java
147 lines (121 loc) · 5.1 KB
/
GeoItem.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package mod.azure.azurelib.animatable;
import java.util.EnumMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import mod.azure.azurelib.platform.Services;
import org.jetbrains.annotations.Nullable;
import com.google.common.base.Suppliers;
import mod.azure.azurelib.cache.AnimatableIdCache;
import mod.azure.azurelib.constant.DataTickets;
import mod.azure.azurelib.core.animatable.GeoAnimatable;
import mod.azure.azurelib.core.animatable.instance.AnimatableInstanceCache;
import mod.azure.azurelib.core.animatable.instance.SingletonAnimatableInstanceCache;
import mod.azure.azurelib.core.animation.AnimatableManager;
import mod.azure.azurelib.core.animation.ContextAwareAnimatableManager;
import mod.azure.azurelib.util.RenderUtils;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemDisplayContext;
import net.minecraft.world.item.ItemStack;
/**
* The {@link mod.azure.azurelib.core.animatable.GeoAnimatable GeoAnimatable} interface specific to {@link net.minecraft.world.item.Item Items}. This also applies to armor, as they are just items too.
*/
public interface GeoItem extends SingletonGeoAnimatable {
String ID_NBT_KEY = "AzureLibID";
/**
* Safety wrapper to distance the client-side code from common code.<br>
* This should be cached in your {@link net.minecraft.world.item.Item Item} class
*/
static Supplier<Object> makeRenderer(GeoItem item) {
if (Services.PLATFORM.isServerEnvironment())
return () -> null;
return Suppliers.memoize(() -> {
AtomicReference<Object> renderProvider = new AtomicReference<>();
item.createRenderer(renderProvider::set);
return renderProvider.get();
});
}
/**
* Gets the unique identifying number from this ItemStack's {@link net.minecraft.nbt.Tag NBT}, or {@link Long#MAX_VALUE} if one hasn't been assigned
*/
static long getId(ItemStack stack) {
CompoundTag tag = stack.getTag();
if (tag == null)
return Long.MAX_VALUE;
return tag.getLong(ID_NBT_KEY);
}
/**
* Gets the unique identifying number from this ItemStack's {@link net.minecraft.nbt.Tag NBT}.<br>
* If no ID has been reserved for this stack yet, it will reserve a new id and assign it
*/
static long getOrAssignId(ItemStack stack, ServerLevel level) {
CompoundTag tag = stack.getOrCreateTag();
long id = tag.getLong(ID_NBT_KEY);
if (tag.contains(ID_NBT_KEY, Tag.TAG_ANY_NUMERIC))
return id;
id = AnimatableIdCache.getFreeId(level);
tag.putLong(ID_NBT_KEY, id);
return id;
}
/**
* Returns the current age/tick of the animatable instance.<br>
* By default this is just the animatable's age in ticks, but this method allows for non-ticking custom animatables to provide their own values
*
* @param itemStack The ItemStack representing this animatable
* @return The current tick/age of the animatable, for animation purposes
*/
@Override
default double getTick(Object itemStack) {
return RenderUtils.getCurrentTick();
}
/**
* Whether this item animatable is perspective aware, handling animations differently depending on the {@link net.minecraft.world.item.ItemDisplayContext render perspective}
*/
default boolean isPerspectiveAware() {
return false;
}
/**
* Replaces the default AnimatableInstanceCache for GeoItems if {@link GeoItem#isPerspectiveAware()} is true, for perspective-dependent handling
*/
@Nullable
@Override
default AnimatableInstanceCache animatableCacheOverride() {
if (isPerspectiveAware())
return new ContextBasedAnimatableInstanceCache(this);
return SingletonGeoAnimatable.super.animatableCacheOverride();
}
/**
* AnimatableInstanceCache specific to GeoItems, for doing render perspective based animations
*/
class ContextBasedAnimatableInstanceCache extends SingletonAnimatableInstanceCache {
public ContextBasedAnimatableInstanceCache(GeoAnimatable animatable) {
super(animatable);
}
/**
* Gets an {@link AnimatableManager} instance from this cache, cached under the id provided, or a new one if one doesn't already exist.<br>
* This subclass assumes that all animatable instances will be sharing this cache instance, and so differentiates data by ids.
*/
@Override
public AnimatableManager<?> getManagerForId(long uniqueId) {
if (!this.managers.containsKey(uniqueId))
this.managers.put(uniqueId, new ContextAwareAnimatableManager<GeoItem, ItemDisplayContext>(this.animatable) {
@Override
protected Map<ItemDisplayContext, AnimatableManager<GeoItem>> buildContextOptions(GeoAnimatable animatable) {
Map<ItemDisplayContext, AnimatableManager<GeoItem>> map = new EnumMap<>(ItemDisplayContext.class);
for (ItemDisplayContext context : ItemDisplayContext.values()) {
map.put(context, new AnimatableManager<>(animatable));
}
return map;
}
@Override
public ItemDisplayContext getCurrentContext() {
ItemDisplayContext context = getData(DataTickets.ITEM_RENDER_PERSPECTIVE);
return context == null ? ItemDisplayContext.NONE : context;
}
});
return this.managers.get(uniqueId);
}
}
}