hytale-server/com/hypixel/hytale/assetstore/map/DefaultAssetMap.java

504 lines
16 KiB
Java

package com.hypixel.hytale.assetstore.map;
import com.hypixel.fastutil.ints.Int2ObjectConcurrentHashMap;
import com.hypixel.hytale.assetstore.AssetExtraInfo;
import com.hypixel.hytale.assetstore.AssetMap;
import com.hypixel.hytale.assetstore.JsonAsset;
import com.hypixel.hytale.assetstore.codec.AssetCodec;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.IntSets;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectSet;
import it.unimi.dsi.fastutil.objects.ObjectSets;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.StampedLock;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class DefaultAssetMap<K, T extends JsonAsset<K>> extends AssetMap<K, T> {
public static final DefaultAssetMap.AssetRef[] EMPTY_PAIR_ARRAY = new DefaultAssetMap.AssetRef[0];
public static final String DEFAULT_PACK_KEY = "Hytale:Hytale";
protected final StampedLock assetMapLock = new StampedLock();
@Nonnull
protected final Map<K, T> assetMap;
protected final Map<K, DefaultAssetMap.AssetRef<T>[]> assetChainMap;
protected final Map<String, ObjectSet<K>> packAssetKeys = new ConcurrentHashMap<>();
protected final Map<Path, ObjectSet<K>> pathToKeyMap = new ConcurrentHashMap<>();
protected final Map<K, ObjectSet<K>> assetChildren;
protected final Int2ObjectConcurrentHashMap<Set<K>> tagStorage = new Int2ObjectConcurrentHashMap<>();
protected final Int2ObjectConcurrentHashMap<Set<K>> unmodifiableTagStorage = new Int2ObjectConcurrentHashMap<>();
protected final IntSet unmodifiableTagKeys = IntSets.unmodifiable(this.tagStorage.keySet());
public DefaultAssetMap() {
this.assetMap = new Object2ObjectOpenCustomHashMap(CaseInsensitiveHashStrategy.getInstance());
this.assetChainMap = new Object2ObjectOpenCustomHashMap(CaseInsensitiveHashStrategy.getInstance());
this.assetChildren = new Object2ObjectOpenCustomHashMap(CaseInsensitiveHashStrategy.getInstance());
}
public DefaultAssetMap(@Nonnull Map<K, T> assetMap) {
this.assetMap = assetMap;
this.assetChainMap = new Object2ObjectOpenCustomHashMap(CaseInsensitiveHashStrategy.getInstance());
this.assetChildren = new Object2ObjectOpenCustomHashMap(CaseInsensitiveHashStrategy.getInstance());
}
@Nullable
@Override
public T getAsset(K key) {
long stamp = this.assetMapLock.tryOptimisticRead();
T value = this.assetMap.get(key);
if (this.assetMapLock.validate(stamp)) {
return value;
} else {
stamp = this.assetMapLock.readLock();
JsonAsset var5;
try {
var5 = this.assetMap.get(key);
} finally {
this.assetMapLock.unlockRead(stamp);
}
return (T)var5;
}
}
@Nullable
@Override
public T getAsset(@Nonnull String packKey, K key) {
long stamp = this.assetMapLock.tryOptimisticRead();
T result = this.getAssetForPack0(packKey, key);
if (this.assetMapLock.validate(stamp)) {
return result;
} else {
stamp = this.assetMapLock.readLock();
JsonAsset var6;
try {
var6 = this.getAssetForPack0(packKey, key);
} finally {
this.assetMapLock.unlockRead(stamp);
}
return (T)var6;
}
}
private T getAssetForPack0(@Nonnull String packKey, K key) {
DefaultAssetMap.AssetRef<T>[] chain = this.assetChainMap.get(key);
if (chain == null) {
return null;
} else {
for (int i = 0; i < chain.length; i++) {
DefaultAssetMap.AssetRef<T> pair = chain[i];
if (Objects.equals(pair.pack, packKey)) {
if (i == 0) {
return null;
}
return chain[i - 1].value;
}
}
return this.assetMap.get(key);
}
}
@Nullable
@Override
public Path getPath(K key) {
long stamp = this.assetMapLock.tryOptimisticRead();
Path result = this.getPath0(key);
if (this.assetMapLock.validate(stamp)) {
return result;
} else {
stamp = this.assetMapLock.readLock();
Path var5;
try {
var5 = this.getPath0(key);
} finally {
this.assetMapLock.unlockRead(stamp);
}
return var5;
}
}
@Nullable
@Override
public String getAssetPack(K key) {
long stamp = this.assetMapLock.tryOptimisticRead();
String result = this.getAssetPack0(key);
if (this.assetMapLock.validate(stamp)) {
return result;
} else {
stamp = this.assetMapLock.readLock();
String var5;
try {
var5 = this.getAssetPack0(key);
} finally {
this.assetMapLock.unlockRead(stamp);
}
return var5;
}
}
@Nullable
private Path getPath0(K key) {
DefaultAssetMap.AssetRef<T> result = this.getAssetRef(key);
return result != null ? result.path : null;
}
@Nullable
private String getAssetPack0(K key) {
DefaultAssetMap.AssetRef<T> result = this.getAssetRef(key);
return result != null ? result.pack : null;
}
@Nullable
private DefaultAssetMap.AssetRef<T> getAssetRef(K key) {
DefaultAssetMap.AssetRef<T>[] chain = this.assetChainMap.get(key);
return chain == null ? null : chain[chain.length - 1];
}
@Override
public Set<K> getKeys(@Nonnull Path path) {
ObjectSet<K> set = this.pathToKeyMap.get(path);
return set == null ? ObjectSets.emptySet() : ObjectSets.unmodifiable(set);
}
@Override
public Set<K> getChildren(K key) {
long stamp = this.assetMapLock.tryOptimisticRead();
ObjectSet<K> children = this.assetChildren.get(key);
Set<K> result = children == null ? ObjectSets.emptySet() : ObjectSets.unmodifiable(children);
if (this.assetMapLock.validate(stamp)) {
return result;
} else {
stamp = this.assetMapLock.readLock();
ObjectSet var6;
try {
children = this.assetChildren.get(key);
var6 = children == null ? ObjectSets.emptySet() : ObjectSets.unmodifiable(children);
} finally {
this.assetMapLock.unlockRead(stamp);
}
return var6;
}
}
@Override
public int getAssetCount() {
return this.assetMap.size();
}
@Nonnull
@Override
public Map<K, T> getAssetMap() {
return Collections.unmodifiableMap(this.assetMap);
}
@Nonnull
@Override
public Map<K, Path> getPathMap(@Nonnull String packKey) {
long stamp = this.assetMapLock.readLock();
Map var4;
try {
var4 = this.assetChainMap
.entrySet()
.stream()
.map(e -> Map.entry(e.getKey(), Arrays.stream(e.getValue()).filter(v -> Objects.equals(v.pack, packKey)).findFirst()))
.filter(e -> e.getValue().isPresent())
.filter(e -> e.getValue().get().path != null)
.collect(Collectors.toMap(Entry::getKey, e -> e.getValue().get().path));
} finally {
this.assetMapLock.unlockRead(stamp);
}
return var4;
}
@Override
public Set<K> getKeysForTag(int tagIndex) {
return this.unmodifiableTagStorage.getOrDefault(tagIndex, ObjectSets.emptySet());
}
@Nonnull
@Override
public IntSet getTagIndexes() {
return this.unmodifiableTagKeys;
}
@Override
public int getTagCount() {
return this.tagStorage.size();
}
@Override
protected void clear() {
long stamp = this.assetMapLock.writeLock();
try {
this.assetChildren.clear();
this.assetChainMap.clear();
this.pathToKeyMap.clear();
this.assetMap.clear();
this.tagStorage.clear();
this.unmodifiableTagStorage.clear();
} finally {
this.assetMapLock.unlockWrite(stamp);
}
}
@Override
protected void putAll(
@Nonnull String packKey,
@Nonnull AssetCodec<K, T> codec,
@Nonnull Map<K, T> loadedAssets,
@Nonnull Map<K, Path> loadedKeyToPathMap,
@Nonnull Map<K, Set<K>> loadedAssetChildren
) {
long stamp = this.assetMapLock.writeLock();
try {
for (Entry<K, Set<K>> entry : loadedAssetChildren.entrySet()) {
this.assetChildren.computeIfAbsent(entry.getKey(), k -> new ObjectOpenHashSet(3)).addAll(entry.getValue());
}
for (Entry<K, Path> entry : loadedKeyToPathMap.entrySet()) {
this.pathToKeyMap.computeIfAbsent(entry.getValue(), k -> new ObjectOpenHashSet(1)).add(entry.getKey());
}
for (Entry<K, T> e : loadedAssets.entrySet()) {
K key = e.getKey();
this.packAssetKeys.computeIfAbsent(packKey, v -> new ObjectOpenHashSet()).add(key);
DefaultAssetMap.AssetRef<T>[] chain = this.assetChainMap.get(key);
if (chain == null) {
chain = EMPTY_PAIR_ARRAY;
}
boolean found = false;
DefaultAssetMap.AssetRef[] finalVal = chain;
int var14 = chain.length;
int var15 = 0;
while (true) {
if (var15 < var14) {
DefaultAssetMap.AssetRef<T> pair = finalVal[var15];
if (!Objects.equals(pair.pack, packKey)) {
var15++;
continue;
}
pair.value = e.getValue();
found = true;
}
if (!found) {
chain = Arrays.copyOf(chain, chain.length + 1);
chain[chain.length - 1] = new DefaultAssetMap.AssetRef<>(packKey, loadedKeyToPathMap.get(e.getKey()), e.getValue());
this.assetChainMap.put(key, chain);
}
T finalValx = chain[chain.length - 1].value;
this.assetMap.put(key, finalValx);
break;
}
}
} finally {
this.assetMapLock.unlockWrite(stamp);
}
this.putAssetTags(codec, loadedAssets);
}
protected void putAssetTags(@Nonnull AssetCodec<K, T> codec, @Nonnull Map<K, T> loadedAssets) {
for (Entry<K, T> entry : loadedAssets.entrySet()) {
AssetExtraInfo.Data data = codec.getData(entry.getValue());
if (data != null) {
K key = entry.getKey();
IntIterator iterator = data.getExpandedTagIndexes().iterator();
while (iterator.hasNext()) {
int tag = iterator.nextInt();
this.putAssetTag(key, tag);
}
}
}
}
protected void putAssetTag(K key, int tag) {
this.tagStorage.computeIfAbsent(tag, k -> {
ObjectOpenHashSet<K> set = new ObjectOpenHashSet(3);
this.unmodifiableTagStorage.put(k, ObjectSets.unmodifiable(set));
return set;
}).add(key);
}
@Override
public Set<K> getKeysForPack(@Nonnull String name) {
return (Set<K>)this.packAssetKeys.get(name);
}
@Override
protected Set<K> remove(@Nonnull Set<K> keys) {
long stamp = this.assetMapLock.writeLock();
Object var16;
try {
Set<K> children = new HashSet<>();
for (K key : keys) {
DefaultAssetMap.AssetRef<T>[] chain = this.assetChainMap.remove(key);
if (chain != null) {
DefaultAssetMap.AssetRef<T> info = chain[chain.length - 1];
if (info.path != null) {
this.pathToKeyMap.computeIfPresent(info.path, (p, list) -> {
list.remove(key);
return list.isEmpty() ? null : list;
});
}
this.assetMap.remove(key);
for (DefaultAssetMap.AssetRef<T> c : chain) {
this.packAssetKeys.get(Objects.requireNonNullElse(c.pack, "Hytale:Hytale")).remove(key);
}
for (ObjectSet<K> child : this.assetChildren.values()) {
child.remove(key);
}
ObjectSet<K> child = this.assetChildren.remove(key);
if (child != null) {
children.addAll(child);
}
}
}
this.tagStorage.forEach((_k, value, removedKeys) -> value.removeAll(removedKeys), keys);
children.removeAll(keys);
var16 = children;
} finally {
this.assetMapLock.unlockWrite(stamp);
}
return (Set<K>)var16;
}
@Override
protected Set<K> remove(@Nonnull String packKey, @Nonnull Set<K> keys, @Nonnull List<Entry<String, Object>> pathsToReload) {
long stamp = this.assetMapLock.writeLock();
Set iterator;
try {
Set<K> children = new HashSet<>();
ObjectSet<K> packKeys = this.packAssetKeys.get(Objects.requireNonNullElse(packKey, "Hytale:Hytale"));
if (packKeys != null) {
Iterator<K> iteratorx = keys.iterator();
while (iteratorx.hasNext()) {
K key = iteratorx.next();
packKeys.remove(key);
DefaultAssetMap.AssetRef<T>[] chain = this.assetChainMap.remove(key);
if (chain.length == 1) {
DefaultAssetMap.AssetRef<T> info = chain[0];
if (!Objects.equals(info.pack, packKey)) {
iteratorx.remove();
this.assetChainMap.put(key, chain);
} else {
if (info.path != null) {
this.pathToKeyMap.computeIfPresent(info.path, (p, list) -> {
list.remove(key);
return list.isEmpty() ? null : list;
});
}
this.assetMap.remove(key);
for (ObjectSet<K> child : this.assetChildren.values()) {
child.remove(key);
}
ObjectSet<K> child = this.assetChildren.remove(key);
if (child != null) {
children.addAll(child);
}
}
} else {
iteratorx.remove();
DefaultAssetMap.AssetRef<T>[] newChain = new DefaultAssetMap.AssetRef[chain.length - 1];
int offset = 0;
for (int i = 0; i < chain.length; i++) {
DefaultAssetMap.AssetRef<T> pair = chain[i];
if (Objects.equals(pair.pack, packKey)) {
if (pair.path != null) {
this.pathToKeyMap.computeIfPresent(pair.path, (p, list) -> {
list.remove(key);
return list.isEmpty() ? null : list;
});
}
} else {
newChain[offset++] = pair;
if (pair.path != null) {
pathsToReload.add(Map.entry(pair.pack, pair.path));
} else {
pathsToReload.add(Map.entry(pair.pack, pair.value));
}
}
}
this.assetChainMap.put(key, newChain);
DefaultAssetMap.AssetRef<T> newAsset = newChain[newChain.length - 1];
this.assetMap.put(key, newAsset.value);
if (newAsset.path != null) {
this.pathToKeyMap.computeIfAbsent(newAsset.path, k -> new ObjectOpenHashSet(1)).add(key);
}
}
}
this.tagStorage.forEach((_k, value, removedKeys) -> value.removeAll(removedKeys), keys);
children.removeAll(keys);
return children;
}
iterator = Collections.emptySet();
} finally {
this.assetMapLock.unlockWrite(stamp);
}
return iterator;
}
protected static class AssetRef<T> {
protected final String pack;
protected final Path path;
protected T value;
protected AssetRef(String pack, Path path, T value) {
this.pack = pack;
this.path = path;
this.value = value;
}
}
}