-
Notifications
You must be signed in to change notification settings - Fork 753
/
SlotType.java
261 lines (230 loc) · 8.61 KB
/
SlotType.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
package slimeknights.tconstruct.library.tools;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.TextColor;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import slimeknights.mantle.data.loadable.Loadable;
import slimeknights.mantle.data.loadable.field.LoadableField;
import slimeknights.mantle.data.loadable.primitive.StringLoadable;
import slimeknights.tconstruct.TConstruct;
import slimeknights.tconstruct.library.utils.JsonUtils;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.regex.Pattern;
/**
* Class handling slot types for modifiers
*/
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public final class SlotType {
/** Loadable for a slot type */
public static final StringLoadable<SlotType> LOADABLE = StringLoadable.DEFAULT.comapFlatMap((name, error) -> {
if (!isValidName(name)) {
throw error.create("Invalid slot type name '" + name + '\'');
}
return SlotType.getOrCreate(name);
}, SlotType::getName);
/** Key for uppercase slot name */
private static final String KEY_PREFIX = TConstruct.makeTranslationKey("stat", "slot.prefix.");
/** Key for lowercase slot name */
private static final String KEY_DISPLAY = TConstruct.makeTranslationKey("stat", "slot.display.");
/** Map of instances for each name */
private static final Map<String,SlotType> SLOT_TYPES = new HashMap<>();
/** List of all slots in the order they were added */
private static final List<SlotType> ALL_SLOTS = new ArrayList<>();
/** Regex to validate slot type strings */
private static final Pattern VALIDATOR = Pattern.compile("^[a-z0-9_]*$");
/** Common slot type for modifiers with many levels */
public static final SlotType UPGRADE = create("upgrades", 0xFFCCBA47);
/** Slot type for protection based modifiers on armor */
public static final SlotType DEFENSE = create("defense", 0xFFA8FFA0);
/** Rare slot type for powerful and rather exclusive modifiers */
public static final SlotType ABILITY = create("abilities", 0xFFB8A0FF);
/** Slot type used in the soul forge */
public static final SlotType SOUL = create("souls", -1);
/** Just makes sure static initialization is done early enough */
public static void init() {}
/** Checks if the given slot name is valid */
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
public static boolean isValidName(String name) {
return VALIDATOR.matcher(name).matches();
}
/**
* Registers the given slot type.
* Note that you will also want to define a texture for the creative modifier and JEI using {@link slimeknights.mantle.client.model.NBTKeyModel#registerExtraTexture(ResourceLocation, String, ResourceLocation)}
* @param name Name of the slot type
* @param color Color of the slot
* @return Slot type instance for the name, only once instance for each name
* @apiNote
* @throws IllegalArgumentException Error if a name is invalid
*/
public static SlotType create(String name, int color) {
if (SLOT_TYPES.containsKey(name)) {
return SLOT_TYPES.get(name);
}
if (!isValidName(name)) {
throw new IllegalArgumentException("Non [a-z0-9_] character in slot name: " + name);
}
SlotType type = new SlotType(name, TextColor.fromRgb(color));
SLOT_TYPES.put(name, type);
ALL_SLOTS.add(type);
return type;
}
/** Gets an existing slot type, or creates it if missing */
public static SlotType getOrCreate(String name) {
return create(name, -1);
}
/**
* Gets the slot type for the given name, if present
* @param name Name
* @return Type name
*/
@Nullable
public static SlotType getIfPresent(String name) {
return SLOT_TYPES.get(name);
}
/** Reads the slot type from the packet buffer */
public static SlotType read(FriendlyByteBuf buffer) {
return getOrCreate(buffer.readUtf());
}
/**
* Gets a collection of all registered slot types. Persists between worlds, so a slot type existing does not mean its used
* @return Collection of all slot types
*/
public static Collection<SlotType> getAllSlotTypes() {
return ALL_SLOTS;
}
/** Name of this slot type, used for serialization */
@Getter
private final String name;
/** Gets the color of this slot type */
@Getter
private final TextColor color;
/** Cached text component display names */
private Component displayName = null;
/** Gets the display name for display in a title */
public String getPrefix() {
return KEY_PREFIX + name;
}
/** Gets the display name for display in a sentence */
public Component getDisplayName() {
if (displayName == null) {
displayName = Component.translatable(KEY_DISPLAY + name);
}
return displayName;
}
/** Writes this slot type to the packet buffer */
public void write(FriendlyByteBuf buffer) {
buffer.writeUtf(name);
}
@Override
public String toString() {
return "SlotType{" + name + '}';
}
/** Data object representing a slot type and count */
public record SlotCount(SlotType type, int count) {
public static final Loadable<SlotCount> LOADABLE = new Loadable<>() {
@Override
public SlotCount convert(JsonElement element, String key) {
JsonObject json = GsonHelper.convertToJsonObject(element, key);
if (json.entrySet().size() != 1) {
throw new JsonSyntaxException("Cannot set multiple slot types");
}
Entry<String,JsonElement> entry = json.entrySet().iterator().next();
String typeString = entry.getKey();
if (!SlotType.isValidName(typeString)) {
throw new JsonSyntaxException("Invalid slot type name '" + typeString + "'");
}
SlotType slotType = SlotType.getOrCreate(typeString);
int slots = JsonUtils.convertToIntMin(entry.getValue(), "count", 1);
return new SlotCount(slotType, slots);
}
@Override
public JsonElement serialize(SlotCount slots) {
JsonObject json = new JsonObject();
json.addProperty(slots.type.getName(), slots.count);
return json;
}
@Override
public SlotCount decode(FriendlyByteBuf buffer) {
return new SlotCount(SlotType.read(buffer), buffer.readVarInt());
}
@Override
public void encode(FriendlyByteBuf buffer, SlotCount slots) {
buffer.writeVarInt(slots.count());
slots.type().write(buffer);
}
@Override
public <P> LoadableField<SlotCount,P> nullableField(String key, Function<P,SlotCount> getter) {
return new NullableSlotCountField<>(key, getter);
}
};
/** Nullable field which compacts slot counts in the buffer */
private record NullableSlotCountField<P>(String key, Function<P,SlotCount> getter) implements LoadableField<SlotCount,P> {
@Nullable
@Override
public SlotCount get(JsonObject json) {
return LOADABLE.getOrDefault(json, key, null);
}
@Override
public void serialize(P parent, JsonObject json) {
SlotCount count = getter.apply(parent);
if (count != null) {
json.add(key, LOADABLE.serialize(count));
}
}
@Nullable
@Override
public SlotCount decode(FriendlyByteBuf buffer) {
int count = buffer.readVarInt();
if (count == 0) {
return null;
}
return new SlotCount(SlotType.read(buffer), count);
}
@Override
public void encode(FriendlyByteBuf buffer, P parent) {
SlotCount slotCount = getter.apply(parent);
if (slotCount == null) {
buffer.writeVarInt(0);
} else {
buffer.writeVarInt(slotCount.count);
slotCount.type.write(buffer);
}
}
}
/**
* Gets the type for the given slot count
*/
@Nullable
public static SlotType type(@Nullable SlotCount count) {
if (count == null) {
return null;
}
return count.type();
}
/** Gets the given type of slots from the given slot count object */
public static int get(@Nullable SlotCount slots, SlotType type) {
if (slots != null && slots.type() == type) {
return slots.count();
}
return 0;
}
@Override
public String toString() {
return "SlotCount{" + type.name + ": " + count + '}';
}
}
}