235 lines
7.2 KiB
Java
235 lines
7.2 KiB
Java
package net.minecraft.core.component;
|
|
|
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
|
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
|
|
import it.unimi.dsi.fastutil.objects.Reference2ObjectMaps;
|
|
import it.unimi.dsi.fastutil.objects.ReferenceArraySet;
|
|
import java.util.ArrayList;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import java.util.Map.Entry;
|
|
import java.util.stream.Collectors;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public final class PatchedDataComponentMap implements DataComponentMap {
|
|
private final DataComponentMap prototype;
|
|
private Reference2ObjectMap<DataComponentType<?>, Optional<?>> patch;
|
|
private boolean copyOnWrite;
|
|
|
|
public PatchedDataComponentMap(DataComponentMap prototype) {
|
|
this(prototype, Reference2ObjectMaps.emptyMap(), true);
|
|
}
|
|
|
|
private PatchedDataComponentMap(DataComponentMap prototype, Reference2ObjectMap<DataComponentType<?>, Optional<?>> patch, boolean copyOnWtite) {
|
|
this.prototype = prototype;
|
|
this.patch = patch;
|
|
this.copyOnWrite = copyOnWtite;
|
|
}
|
|
|
|
public static PatchedDataComponentMap fromPatch(DataComponentMap prototype, DataComponentPatch patch) {
|
|
if (isPatchSanitized(prototype, patch.map)) {
|
|
return new PatchedDataComponentMap(prototype, patch.map, true);
|
|
} else {
|
|
PatchedDataComponentMap patchedDataComponentMap = new PatchedDataComponentMap(prototype);
|
|
patchedDataComponentMap.applyPatch(patch);
|
|
return patchedDataComponentMap;
|
|
}
|
|
}
|
|
|
|
private static boolean isPatchSanitized(DataComponentMap prototype, Reference2ObjectMap<DataComponentType<?>, Optional<?>> map) {
|
|
for (Entry<DataComponentType<?>, Optional<?>> entry : Reference2ObjectMaps.fastIterable(map)) {
|
|
Object object = prototype.get((DataComponentType)entry.getKey());
|
|
Optional<?> optional = (Optional<?>)entry.getValue();
|
|
if (optional.isPresent() && optional.get().equals(object)) {
|
|
return false;
|
|
}
|
|
|
|
if (optional.isEmpty() && object == null) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public <T> T get(DataComponentType<? extends T> component) {
|
|
Optional<? extends T> optional = (Optional<? extends T>)this.patch.get(component);
|
|
return (T)(optional != null ? optional.orElse(null) : this.prototype.get(component));
|
|
}
|
|
|
|
public boolean hasNonDefault(DataComponentType<?> component) {
|
|
return this.patch.containsKey(component);
|
|
}
|
|
|
|
@Nullable
|
|
public <T> T set(DataComponentType<T> component, @Nullable T value) {
|
|
this.ensureMapOwnership();
|
|
T object = this.prototype.get(component);
|
|
Optional<T> optional;
|
|
if (Objects.equals(value, object)) {
|
|
optional = (Optional<T>)this.patch.remove(component);
|
|
} else {
|
|
optional = (Optional<T>)this.patch.put(component, Optional.ofNullable(value));
|
|
}
|
|
|
|
return (T)(optional != null ? optional.orElse(object) : object);
|
|
}
|
|
|
|
@Nullable
|
|
public <T> T remove(DataComponentType<? extends T> component) {
|
|
this.ensureMapOwnership();
|
|
T object = this.prototype.get(component);
|
|
Optional<? extends T> optional;
|
|
if (object != null) {
|
|
optional = (Optional<? extends T>)this.patch.put(component, Optional.empty());
|
|
} else {
|
|
optional = (Optional<? extends T>)this.patch.remove(component);
|
|
}
|
|
|
|
return (T)(optional != null ? optional.orElse(null) : object);
|
|
}
|
|
|
|
public void applyPatch(DataComponentPatch patch) {
|
|
this.ensureMapOwnership();
|
|
|
|
for (Entry<DataComponentType<?>, Optional<?>> entry : Reference2ObjectMaps.fastIterable(patch.map)) {
|
|
this.applyPatch((DataComponentType<?>)entry.getKey(), (Optional<?>)entry.getValue());
|
|
}
|
|
}
|
|
|
|
private void applyPatch(DataComponentType<?> component, Optional<?> value) {
|
|
Object object = this.prototype.get(component);
|
|
if (value.isPresent()) {
|
|
if (value.get().equals(object)) {
|
|
this.patch.remove(component);
|
|
} else {
|
|
this.patch.put(component, value);
|
|
}
|
|
} else if (object != null) {
|
|
this.patch.put(component, Optional.empty());
|
|
} else {
|
|
this.patch.remove(component);
|
|
}
|
|
}
|
|
|
|
public void restorePatch(DataComponentPatch patch) {
|
|
this.ensureMapOwnership();
|
|
this.patch.clear();
|
|
this.patch.putAll(patch.map);
|
|
}
|
|
|
|
public void clearPatch() {
|
|
this.ensureMapOwnership();
|
|
this.patch.clear();
|
|
}
|
|
|
|
public void setAll(DataComponentMap map) {
|
|
for (TypedDataComponent<?> typedDataComponent : map) {
|
|
typedDataComponent.applyTo(this);
|
|
}
|
|
}
|
|
|
|
private void ensureMapOwnership() {
|
|
if (this.copyOnWrite) {
|
|
this.patch = new Reference2ObjectArrayMap<>(this.patch);
|
|
this.copyOnWrite = false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Set<DataComponentType<?>> keySet() {
|
|
if (this.patch.isEmpty()) {
|
|
return this.prototype.keySet();
|
|
} else {
|
|
Set<DataComponentType<?>> set = new ReferenceArraySet<>(this.prototype.keySet());
|
|
|
|
for (it.unimi.dsi.fastutil.objects.Reference2ObjectMap.Entry<DataComponentType<?>, Optional<?>> entry : Reference2ObjectMaps.fastIterable(this.patch)) {
|
|
Optional<?> optional = (Optional<?>)entry.getValue();
|
|
if (optional.isPresent()) {
|
|
set.add((DataComponentType)entry.getKey());
|
|
} else {
|
|
set.remove(entry.getKey());
|
|
}
|
|
}
|
|
|
|
return set;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public Iterator<TypedDataComponent<?>> iterator() {
|
|
if (this.patch.isEmpty()) {
|
|
return this.prototype.iterator();
|
|
} else {
|
|
List<TypedDataComponent<?>> list = new ArrayList(this.patch.size() + this.prototype.size());
|
|
|
|
for (it.unimi.dsi.fastutil.objects.Reference2ObjectMap.Entry<DataComponentType<?>, Optional<?>> entry : Reference2ObjectMaps.fastIterable(this.patch)) {
|
|
if (((Optional)entry.getValue()).isPresent()) {
|
|
list.add(TypedDataComponent.createUnchecked((DataComponentType)entry.getKey(), ((Optional)entry.getValue()).get()));
|
|
}
|
|
}
|
|
|
|
for (TypedDataComponent<?> typedDataComponent : this.prototype) {
|
|
if (!this.patch.containsKey(typedDataComponent.type())) {
|
|
list.add(typedDataComponent);
|
|
}
|
|
}
|
|
|
|
return list.iterator();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int size() {
|
|
int i = this.prototype.size();
|
|
|
|
for (it.unimi.dsi.fastutil.objects.Reference2ObjectMap.Entry<DataComponentType<?>, Optional<?>> entry : Reference2ObjectMaps.fastIterable(this.patch)) {
|
|
boolean bl = ((Optional)entry.getValue()).isPresent();
|
|
boolean bl2 = this.prototype.has((DataComponentType<?>)entry.getKey());
|
|
if (bl != bl2) {
|
|
i += bl ? 1 : -1;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
public DataComponentPatch asPatch() {
|
|
if (this.patch.isEmpty()) {
|
|
return DataComponentPatch.EMPTY;
|
|
} else {
|
|
this.copyOnWrite = true;
|
|
return new DataComponentPatch(this.patch);
|
|
}
|
|
}
|
|
|
|
public PatchedDataComponentMap copy() {
|
|
this.copyOnWrite = true;
|
|
return new PatchedDataComponentMap(this.prototype, this.patch, true);
|
|
}
|
|
|
|
public DataComponentMap toImmutableMap() {
|
|
return (DataComponentMap)(this.patch.isEmpty() ? this.prototype : this.copy());
|
|
}
|
|
|
|
public boolean equals(Object object) {
|
|
return this == object
|
|
? true
|
|
: object instanceof PatchedDataComponentMap patchedDataComponentMap
|
|
&& this.prototype.equals(patchedDataComponentMap.prototype)
|
|
&& this.patch.equals(patchedDataComponentMap.patch);
|
|
}
|
|
|
|
public int hashCode() {
|
|
return this.prototype.hashCode() + this.patch.hashCode() * 31;
|
|
}
|
|
|
|
public String toString() {
|
|
return "{" + (String)this.stream().map(TypedDataComponent::toString).collect(Collectors.joining(", ")) + "}";
|
|
}
|
|
}
|