minecraft-src/net/minecraft/core/component/DataComponentPatch.java
2025-07-04 03:45:38 +03:00

306 lines
11 KiB
Java

package net.minecraft.core.component;
import com.google.common.collect.Sets;
import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectMaps;
import java.util.Optional;
import java.util.Set;
import java.util.Map.Entry;
import java.util.function.Predicate;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Unit;
import org.jetbrains.annotations.Nullable;
public final class DataComponentPatch {
public static final DataComponentPatch EMPTY = new DataComponentPatch(Reference2ObjectMaps.emptyMap());
public static final Codec<DataComponentPatch> CODEC = Codec.dispatchedMap(DataComponentPatch.PatchKey.CODEC, DataComponentPatch.PatchKey::valueCodec)
.xmap(map -> {
if (map.isEmpty()) {
return EMPTY;
} else {
Reference2ObjectMap<DataComponentType<?>, Optional<?>> reference2ObjectMap = new Reference2ObjectArrayMap<>(map.size());
for (Entry<DataComponentPatch.PatchKey, ?> entry : map.entrySet()) {
DataComponentPatch.PatchKey patchKey = (DataComponentPatch.PatchKey)entry.getKey();
if (patchKey.removed()) {
reference2ObjectMap.put(patchKey.type(), Optional.empty());
} else {
reference2ObjectMap.put(patchKey.type(), Optional.of(entry.getValue()));
}
}
return new DataComponentPatch(reference2ObjectMap);
}
}, dataComponentPatch -> {
Reference2ObjectMap<DataComponentPatch.PatchKey, Object> reference2ObjectMap = new Reference2ObjectArrayMap<>(dataComponentPatch.map.size());
for (Entry<DataComponentType<?>, Optional<?>> entry : Reference2ObjectMaps.fastIterable(dataComponentPatch.map)) {
DataComponentType<?> dataComponentType = (DataComponentType<?>)entry.getKey();
if (!dataComponentType.isTransient()) {
Optional<?> optional = (Optional<?>)entry.getValue();
if (optional.isPresent()) {
reference2ObjectMap.put(new DataComponentPatch.PatchKey(dataComponentType, false), optional.get());
} else {
reference2ObjectMap.put(new DataComponentPatch.PatchKey(dataComponentType, true), Unit.INSTANCE);
}
}
}
return reference2ObjectMap;
});
public static final StreamCodec<RegistryFriendlyByteBuf, DataComponentPatch> STREAM_CODEC = createStreamCodec(new DataComponentPatch.CodecGetter() {
@Override
public <T> StreamCodec<RegistryFriendlyByteBuf, T> apply(DataComponentType<T> dataComponentType) {
return dataComponentType.streamCodec().cast();
}
});
public static final StreamCodec<RegistryFriendlyByteBuf, DataComponentPatch> DELIMITED_STREAM_CODEC = createStreamCodec(new DataComponentPatch.CodecGetter() {
@Override
public <T> StreamCodec<RegistryFriendlyByteBuf, T> apply(DataComponentType<T> dataComponentType) {
StreamCodec<RegistryFriendlyByteBuf, T> streamCodec = dataComponentType.streamCodec().cast();
return streamCodec.apply(ByteBufCodecs.lengthPrefixed(Integer.MAX_VALUE));
}
});
private static final String REMOVED_PREFIX = "!";
final Reference2ObjectMap<DataComponentType<?>, Optional<?>> map;
private static StreamCodec<RegistryFriendlyByteBuf, DataComponentPatch> createStreamCodec(DataComponentPatch.CodecGetter codecGetter) {
return new StreamCodec<RegistryFriendlyByteBuf, DataComponentPatch>() {
public DataComponentPatch decode(RegistryFriendlyByteBuf registryFriendlyByteBuf) {
int i = registryFriendlyByteBuf.readVarInt();
int j = registryFriendlyByteBuf.readVarInt();
if (i == 0 && j == 0) {
return DataComponentPatch.EMPTY;
} else {
int k = i + j;
Reference2ObjectMap<DataComponentType<?>, Optional<?>> reference2ObjectMap = new Reference2ObjectArrayMap<>(Math.min(k, 65536));
for (int l = 0; l < i; l++) {
DataComponentType<?> dataComponentType = DataComponentType.STREAM_CODEC.decode(registryFriendlyByteBuf);
Object object = codecGetter.apply(dataComponentType).decode(registryFriendlyByteBuf);
reference2ObjectMap.put(dataComponentType, Optional.of(object));
}
for (int l = 0; l < j; l++) {
DataComponentType<?> dataComponentType = DataComponentType.STREAM_CODEC.decode(registryFriendlyByteBuf);
reference2ObjectMap.put(dataComponentType, Optional.empty());
}
return new DataComponentPatch(reference2ObjectMap);
}
}
public void encode(RegistryFriendlyByteBuf registryFriendlyByteBuf, DataComponentPatch dataComponentPatch) {
if (dataComponentPatch.isEmpty()) {
registryFriendlyByteBuf.writeVarInt(0);
registryFriendlyByteBuf.writeVarInt(0);
} else {
int i = 0;
int j = 0;
for (it.unimi.dsi.fastutil.objects.Reference2ObjectMap.Entry<DataComponentType<?>, Optional<?>> entry : Reference2ObjectMaps.fastIterable(
dataComponentPatch.map
)) {
if (((Optional)entry.getValue()).isPresent()) {
i++;
} else {
j++;
}
}
registryFriendlyByteBuf.writeVarInt(i);
registryFriendlyByteBuf.writeVarInt(j);
for (it.unimi.dsi.fastutil.objects.Reference2ObjectMap.Entry<DataComponentType<?>, Optional<?>> entryx : Reference2ObjectMaps.fastIterable(
dataComponentPatch.map
)) {
Optional<?> optional = (Optional<?>)entryx.getValue();
if (optional.isPresent()) {
DataComponentType<?> dataComponentType = (DataComponentType<?>)entryx.getKey();
DataComponentType.STREAM_CODEC.encode(registryFriendlyByteBuf, dataComponentType);
this.encodeComponent(registryFriendlyByteBuf, dataComponentType, optional.get());
}
}
for (it.unimi.dsi.fastutil.objects.Reference2ObjectMap.Entry<DataComponentType<?>, Optional<?>> entryxx : Reference2ObjectMaps.fastIterable(
dataComponentPatch.map
)) {
if (((Optional)entryxx.getValue()).isEmpty()) {
DataComponentType<?> dataComponentType2 = (DataComponentType<?>)entryxx.getKey();
DataComponentType.STREAM_CODEC.encode(registryFriendlyByteBuf, dataComponentType2);
}
}
}
}
private <T> void encodeComponent(RegistryFriendlyByteBuf buffer, DataComponentType<T> component, Object value) {
codecGetter.apply(component).encode(buffer, (T)value);
}
};
}
DataComponentPatch(Reference2ObjectMap<DataComponentType<?>, Optional<?>> map) {
this.map = map;
}
public static DataComponentPatch.Builder builder() {
return new DataComponentPatch.Builder();
}
@Nullable
public <T> Optional<? extends T> get(DataComponentType<? extends T> component) {
return (Optional<? extends T>)this.map.get(component);
}
public Set<Entry<DataComponentType<?>, Optional<?>>> entrySet() {
return this.map.entrySet();
}
public int size() {
return this.map.size();
}
public DataComponentPatch forget(Predicate<DataComponentType<?>> predicate) {
if (this.isEmpty()) {
return EMPTY;
} else {
Reference2ObjectMap<DataComponentType<?>, Optional<?>> reference2ObjectMap = new Reference2ObjectArrayMap<>(this.map);
reference2ObjectMap.keySet().removeIf(predicate);
return reference2ObjectMap.isEmpty() ? EMPTY : new DataComponentPatch(reference2ObjectMap);
}
}
public boolean isEmpty() {
return this.map.isEmpty();
}
public DataComponentPatch.SplitResult split() {
if (this.isEmpty()) {
return DataComponentPatch.SplitResult.EMPTY;
} else {
DataComponentMap.Builder builder = DataComponentMap.builder();
Set<DataComponentType<?>> set = Sets.newIdentityHashSet();
this.map.forEach((dataComponentType, optional) -> {
if (optional.isPresent()) {
builder.setUnchecked(dataComponentType, optional.get());
} else {
set.add(dataComponentType);
}
});
return new DataComponentPatch.SplitResult(builder.build(), set);
}
}
public boolean equals(Object object) {
return this == object ? true : object instanceof DataComponentPatch dataComponentPatch && this.map.equals(dataComponentPatch.map);
}
public int hashCode() {
return this.map.hashCode();
}
public String toString() {
return toString(this.map);
}
static String toString(Reference2ObjectMap<DataComponentType<?>, Optional<?>> map) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append('{');
boolean bl = true;
for (Entry<DataComponentType<?>, Optional<?>> entry : Reference2ObjectMaps.fastIterable(map)) {
if (bl) {
bl = false;
} else {
stringBuilder.append(", ");
}
Optional<?> optional = (Optional<?>)entry.getValue();
if (optional.isPresent()) {
stringBuilder.append(entry.getKey());
stringBuilder.append("=>");
stringBuilder.append(optional.get());
} else {
stringBuilder.append("!");
stringBuilder.append(entry.getKey());
}
}
stringBuilder.append('}');
return stringBuilder.toString();
}
public static class Builder {
private final Reference2ObjectMap<DataComponentType<?>, Optional<?>> map = new Reference2ObjectArrayMap<>();
Builder() {
}
public <T> DataComponentPatch.Builder set(DataComponentType<T> component, T value) {
this.map.put(component, Optional.of(value));
return this;
}
public <T> DataComponentPatch.Builder remove(DataComponentType<T> component) {
this.map.put(component, Optional.empty());
return this;
}
public <T> DataComponentPatch.Builder set(TypedDataComponent<T> component) {
return this.set(component.type(), component.value());
}
public DataComponentPatch build() {
return this.map.isEmpty() ? DataComponentPatch.EMPTY : new DataComponentPatch(this.map);
}
}
@FunctionalInterface
interface CodecGetter {
<T> StreamCodec<? super RegistryFriendlyByteBuf, T> apply(DataComponentType<T> dataComponentType);
}
record PatchKey(DataComponentType<?> type, boolean removed) {
public static final Codec<DataComponentPatch.PatchKey> CODEC = Codec.STRING
.flatXmap(
string -> {
boolean bl = string.startsWith("!");
if (bl) {
string = string.substring("!".length());
}
ResourceLocation resourceLocation = ResourceLocation.tryParse(string);
DataComponentType<?> dataComponentType = BuiltInRegistries.DATA_COMPONENT_TYPE.getValue(resourceLocation);
if (dataComponentType == null) {
return DataResult.error(() -> "No component with type: '" + resourceLocation + "'");
} else {
return dataComponentType.isTransient()
? DataResult.error(() -> "'" + resourceLocation + "' is not a persistent component")
: DataResult.success(new DataComponentPatch.PatchKey(dataComponentType, bl));
}
},
patchKey -> {
DataComponentType<?> dataComponentType = patchKey.type();
ResourceLocation resourceLocation = BuiltInRegistries.DATA_COMPONENT_TYPE.getKey(dataComponentType);
return resourceLocation == null
? DataResult.error(() -> "Unregistered component: " + dataComponentType)
: DataResult.success(patchKey.removed() ? "!" + resourceLocation : resourceLocation.toString());
}
);
public Codec<?> valueCodec() {
return this.removed ? Codec.EMPTY.codec() : this.type.codecOrThrow();
}
}
public record SplitResult(DataComponentMap added, Set<DataComponentType<?>> removed) {
public static final DataComponentPatch.SplitResult EMPTY = new DataComponentPatch.SplitResult(DataComponentMap.EMPTY, Set.of());
}
}