-
Notifications
You must be signed in to change notification settings - Fork 753
/
ComposableModifier.java
144 lines (129 loc) · 6.48 KB
/
ComposableModifier.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
package slimeknights.tconstruct.library.modifiers.dynamic;
import com.google.common.collect.ImmutableList;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import net.minecraft.network.chat.Component;
import slimeknights.mantle.data.loadable.ErrorFactory;
import slimeknights.mantle.data.loadable.primitive.EnumLoadable;
import slimeknights.mantle.data.loadable.primitive.IntLoadable;
import slimeknights.mantle.data.loadable.record.RecordLoadable;
import slimeknights.tconstruct.TConstruct;
import slimeknights.tconstruct.library.modifiers.Modifier;
import slimeknights.tconstruct.library.modifiers.ModifierEntry;
import slimeknights.tconstruct.library.modifiers.ModifierHook;
import slimeknights.tconstruct.library.modifiers.TinkerHooks;
import slimeknights.tconstruct.library.modifiers.impl.BasicModifier;
import slimeknights.tconstruct.library.modifiers.modules.ModifierModule;
import slimeknights.tconstruct.library.modifiers.util.ModifierHookMap;
import slimeknights.tconstruct.library.modifiers.util.ModifierHookMap.WithHooks;
import slimeknights.tconstruct.library.modifiers.util.ModifierLevelDisplay;
import slimeknights.tconstruct.library.tools.nbt.IToolStackView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/** Modifier consisting of many composed hooks, used in datagen as a serialized modifier. */
public class ComposableModifier extends BasicModifier {
public static final RecordLoadable<ComposableModifier> LOADER = RecordLoadable.create(
ModifierLevelDisplay.LOADER.defaultField("level_display", true, m -> m.levelDisplay),
new EnumLoadable<>(TooltipDisplay.class).defaultField("tooltip_display", TooltipDisplay.ALWAYS, true, m -> m.tooltipDisplay),
IntLoadable.ANY_FULL.defaultField("priority", Integer.MIN_VALUE, m -> m.priority),
ModifierModule.WITH_HOOKS.list(0).defaultField("modules", List.of(), m -> m.modules),
ErrorFactory.FIELD,
(level, tooltip, priority, modules, error) -> new ComposableModifier(level, tooltip, priority == Integer.MIN_VALUE ? computePriority(modules) : priority, modules, error));
private final List<WithHooks<ModifierModule>> modules;
/**
* Creates a new instance
* @param levelDisplay Level display
* @param tooltipDisplay Tooltip display
* @param priority If the value is {@link Integer#MIN_VALUE}, assumed unset for datagen
* @param modules Modules for this modifier
*/
protected ComposableModifier(ModifierLevelDisplay levelDisplay, TooltipDisplay tooltipDisplay, int priority, List<WithHooks<ModifierModule>> modules, ErrorFactory error) {
super(ModifierHookMap.createMap(modules, error), levelDisplay, tooltipDisplay, priority);
this.modules = modules;
}
/** Creates a builder instance for datagen */
public static Builder builder() {
return new Builder();
}
@Override
public RecordLoadable<ComposableModifier> getLoader() {
return LOADER;
}
@Override
public Component getDisplayName(IToolStackView tool, ModifierEntry entry) {
return getHook(TinkerHooks.DISPLAY_NAME).getDisplayName(tool, entry, entry.getDisplayName());
}
/** Computes the recommended priority for a set of modifier modules */
private static int computePriority(List<WithHooks<ModifierModule>> modules) {
// poll all modules to find who has a priority preference
List<ModifierModule> priorityModules = new ArrayList<>();
for (WithHooks<ModifierModule> module : modules) {
if (module.module().getPriority() != null) {
priorityModules.add(module.module());
}
}
if (!priorityModules.isEmpty()) {
//noinspection ConstantConditions validated nonnull above
int firstPriority = priorityModules.get(0).getPriority();
// check if any module disagrees with the first priority, if so we need a warning (but not more than one warning)
for (int i = 1; i < priorityModules.size(); i++) {
//noinspection ConstantConditions validated nonnull above
if (priorityModules.get(i).getPriority() != firstPriority) {
TConstruct.LOG.warn("Multiple modules disagree on the preferred priority for composable modifier, choosing priority {}. Set the priority manually to silence this warning. All opinions: \n{}", firstPriority,
priorityModules.stream()
.map(module -> "* " + module + ": " + module.getPriority())
.collect(Collectors.joining("\n")));
break;
}
}
return firstPriority;
}
return Modifier.DEFAULT_PRIORITY;
}
/** Builder for a composable modifier instance */
@SuppressWarnings("UnusedReturnValue") // it's a builder
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
@Accessors(fluent = true)
public static class Builder {
@Setter
private ModifierLevelDisplay levelDisplay = ModifierLevelDisplay.DEFAULT;
@Setter
private TooltipDisplay tooltipDisplay = TooltipDisplay.ALWAYS;
/** {@link Integer#MIN_VALUE} is an internal value used to represent unset for datagen, to distinguish unset from {@link Modifier#DEFAULT_PRIORITY} */
@Setter
private int priority = Integer.MIN_VALUE;
private final ImmutableList.Builder<WithHooks<ModifierModule>> modules = ImmutableList.builder();
/** Adds a module to the builder */
public final Builder addModule(ModifierModule module) {
modules.add(new WithHooks<>(module, Collections.emptyList()));
return this;
}
/** Adds a module to the builder */
public final Builder addModules(ModifierModule... modules) {
for (ModifierModule module : modules) {
addModule(module);
}
return this;
}
/** Adds a module to the builder */
@SuppressWarnings("UnusedReturnValue")
@SafeVarargs
public final <T extends ModifierModule> Builder addModule(T object, ModifierHook<? super T>... hooks) {
modules.add(new WithHooks<>(object, List.of(hooks)));
return this;
}
/** Builds the final instance */
public ComposableModifier build() {
List<WithHooks<ModifierModule>> modules = this.modules.build();
if (priority == Integer.MIN_VALUE) {
// call computePriority if we did not set one so we get the warning if multiple modules wish to set the priority
computePriority(modules);
}
return new ComposableModifier(levelDisplay, tooltipDisplay, priority, modules, ErrorFactory.RUNTIME);
}
}
}