generated: add (generated, scripts)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@ patcher
|
||||
Assets
|
||||
.hytale-downloader-credentials.json
|
||||
Assets.zip
|
||||
Server
|
||||
59
generated/com/hypixel/hytale/LateMain.java
Normal file
59
generated/com/hypixel/hytale/LateMain.java
Normal file
@@ -0,0 +1,59 @@
|
||||
package com.hypixel.hytale;
|
||||
|
||||
import com.hypixel.hytale.logger.HytaleLogger;
|
||||
import com.hypixel.hytale.logger.backend.HytaleFileHandler;
|
||||
import com.hypixel.hytale.logger.backend.HytaleLoggerBackend;
|
||||
import com.hypixel.hytale.logger.sentry.SkipSentryException;
|
||||
import com.hypixel.hytale.server.core.HytaleServer;
|
||||
import com.hypixel.hytale.server.core.HytaleServerConfig;
|
||||
import com.hypixel.hytale.server.core.Options;
|
||||
import io.sentry.Sentry;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class LateMain {
|
||||
public LateMain() {
|
||||
}
|
||||
|
||||
public static void lateMain(String[] args) {
|
||||
try {
|
||||
if (!Options.parse(args)) {
|
||||
HytaleLogger.init();
|
||||
HytaleFileHandler.INSTANCE.enable();
|
||||
HytaleLogger.replaceStd();
|
||||
HytaleLoggerBackend.LOG_LEVEL_LOADER = name -> {
|
||||
for (Entry<String, Level> e : Options.getOptionSet().valuesOf(Options.LOG_LEVELS)) {
|
||||
if (name.equals(e.getKey())) {
|
||||
return e.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
HytaleServer hytaleServer = HytaleServer.get();
|
||||
if (hytaleServer != null) {
|
||||
HytaleServerConfig config = hytaleServer.getConfig();
|
||||
if (config != null) {
|
||||
Level configLevel = config.getLogLevels().get(name);
|
||||
if (configLevel != null) {
|
||||
return configLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Options.getOptionSet().has(Options.SHUTDOWN_AFTER_VALIDATE) ? Level.WARNING : null;
|
||||
};
|
||||
if (Options.getOptionSet().has(Options.SHUTDOWN_AFTER_VALIDATE)) {
|
||||
HytaleLoggerBackend.reloadLogLevels();
|
||||
}
|
||||
|
||||
new HytaleServer();
|
||||
}
|
||||
} catch (Throwable var2) {
|
||||
if (!SkipSentryException.hasSkipSentry(var2)) {
|
||||
Sentry.captureException(var2);
|
||||
}
|
||||
|
||||
var2.printStackTrace();
|
||||
throw new RuntimeException("Failed to create HytaleServer", var2);
|
||||
}
|
||||
}
|
||||
}
|
||||
78
generated/com/hypixel/hytale/Main.java
Normal file
78
generated/com/hypixel/hytale/Main.java
Normal file
@@ -0,0 +1,78 @@
|
||||
package com.hypixel.hytale;
|
||||
|
||||
import com.hypixel.hytale.plugin.early.EarlyPluginLoader;
|
||||
import com.hypixel.hytale.plugin.early.TransformingClassLoader;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Locale;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public final class Main {
|
||||
public Main() {
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Locale.setDefault(Locale.ENGLISH);
|
||||
System.setProperty("java.awt.headless", "true");
|
||||
System.setProperty("file.encoding", "UTF-8");
|
||||
EarlyPluginLoader.loadEarlyPlugins(args);
|
||||
if (EarlyPluginLoader.hasTransformers()) {
|
||||
launchWithTransformingClassLoader(args);
|
||||
} else {
|
||||
LateMain.lateMain(args);
|
||||
}
|
||||
}
|
||||
|
||||
private static void launchWithTransformingClassLoader(@Nonnull String[] args) {
|
||||
try {
|
||||
URL[] urls = getClasspathUrls();
|
||||
ClassLoader appClassLoader = Main.class.getClassLoader();
|
||||
TransformingClassLoader transformingClassLoader = new TransformingClassLoader(
|
||||
urls, EarlyPluginLoader.getTransformers(), appClassLoader.getParent(), appClassLoader
|
||||
);
|
||||
Thread.currentThread().setContextClassLoader(transformingClassLoader);
|
||||
Class<?> lateMainClass = transformingClassLoader.loadClass("com.hypixel.hytale.LateMain");
|
||||
Method mainMethod = lateMainClass.getMethod("lateMain", String[].class);
|
||||
mainMethod.invoke(null, args);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | ClassNotFoundException var6) {
|
||||
throw new RuntimeException("Failed to launch with transforming classloader", var6);
|
||||
} catch (InvocationTargetException var7) {
|
||||
Throwable cause = var7.getCause();
|
||||
if (cause instanceof RuntimeException re) {
|
||||
throw re;
|
||||
} else if (cause instanceof Error err) {
|
||||
throw err;
|
||||
} else {
|
||||
throw new RuntimeException("LateMain.lateMain() threw an exception", cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static URL[] getClasspathUrls() {
|
||||
if (Main.class.getClassLoader() instanceof URLClassLoader urlClassLoader) {
|
||||
return urlClassLoader.getURLs();
|
||||
} else {
|
||||
ObjectArrayList<URL> urls = new ObjectArrayList<>();
|
||||
String classpath = System.getProperty("java.class.path");
|
||||
if (classpath != null && !classpath.isEmpty()) {
|
||||
for (String pathStr : classpath.split(System.getProperty("path.separator"))) {
|
||||
try {
|
||||
Path path = Path.of(pathStr);
|
||||
if (Files.exists(path)) {
|
||||
urls.add(path.toUri().toURL());
|
||||
}
|
||||
} catch (Exception var8) {
|
||||
System.err.println("[EarlyPlugin] Failed to parse classpath entry: " + pathStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return urls.toArray(URL[]::new);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
generated/com/hypixel/hytale/assetstore/AssetConstants.java
Normal file
11
generated/com/hypixel/hytale/assetstore/AssetConstants.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
public class AssetConstants {
|
||||
public static final int EXPECTED_CHILDREN_PER_ASSET = 3;
|
||||
public static final int EXPECTED_ASSETS_PER_PATH = 1;
|
||||
public static final int EXPECTED_VALUES_PER_TAG = 3;
|
||||
public static final int EXPECTED_ASSETS_PER_TAG = 3;
|
||||
|
||||
public AssetConstants() {
|
||||
}
|
||||
}
|
||||
279
generated/com/hypixel/hytale/assetstore/AssetExtraInfo.java
Normal file
279
generated/com/hypixel/hytale/assetstore/AssetExtraInfo.java
Normal file
@@ -0,0 +1,279 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;
|
||||
import com.hypixel.hytale.codec.ExtraInfo;
|
||||
import com.hypixel.hytale.common.util.ArrayUtil;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class AssetExtraInfo<K> extends ExtraInfo {
|
||||
@Nullable
|
||||
private final Path assetPath;
|
||||
private final AssetExtraInfo.Data data;
|
||||
|
||||
public AssetExtraInfo(AssetExtraInfo.Data data) {
|
||||
super(Integer.MAX_VALUE, AssetValidationResults::new);
|
||||
this.assetPath = null;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public AssetExtraInfo(Path assetPath, AssetExtraInfo.Data data) {
|
||||
super(Integer.MAX_VALUE, AssetValidationResults::new);
|
||||
this.assetPath = assetPath;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public String generateKey() {
|
||||
return "*" + this.getKey() + "_" + this.peekKey('_');
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
return (K)this.getData().getKey();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Path getAssetPath() {
|
||||
return this.assetPath;
|
||||
}
|
||||
|
||||
public AssetExtraInfo.Data getData() {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendDetailsTo(@Nonnull StringBuilder sb) {
|
||||
sb.append("Id: ").append(this.getKey()).append("\n");
|
||||
if (this.assetPath != null) {
|
||||
sb.append("Path: ").append(this.assetPath).append("\n");
|
||||
}
|
||||
}
|
||||
|
||||
public AssetValidationResults getValidationResults() {
|
||||
return (AssetValidationResults)super.getValidationResults();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AssetExtraInfo{assetPath=" + this.assetPath + ", data=" + this.data + "} " + super.toString();
|
||||
}
|
||||
|
||||
public static class Data {
|
||||
public static final char TAG_VALUE_SEPARATOR = '=';
|
||||
private Map<Class<? extends JsonAssetWithMap>, List<Object>> containedAssets;
|
||||
private Map<Class<? extends JsonAssetWithMap>, List<RawAsset<Object>>> containedRawAssets;
|
||||
@Nullable
|
||||
private AssetExtraInfo.Data containerData;
|
||||
private Class<? extends JsonAsset<?>> assetClass;
|
||||
private Object key;
|
||||
private Object parentKey;
|
||||
private final Map<String, String[]> rawTags = new HashMap<>(0);
|
||||
private final Int2ObjectMap<IntSet> tagStorage = new Int2ObjectOpenHashMap<>(0);
|
||||
private final Int2ObjectMap<IntSet> unmodifiableTagStorage = new Int2ObjectOpenHashMap<>(0);
|
||||
private final IntSet expandedTagStorage = new IntOpenHashSet(0);
|
||||
private final IntSet unmodifiableExpandedTagStorage = IntSets.unmodifiable(this.expandedTagStorage);
|
||||
|
||||
public <K> Data(Class<? extends JsonAsset<K>> assetClass, K key, K parentKey) {
|
||||
this.assetClass = assetClass;
|
||||
this.key = key;
|
||||
this.parentKey = parentKey;
|
||||
}
|
||||
|
||||
public <K> Data(@Nullable AssetExtraInfo.Data containerData, Class<? extends JsonAsset<K>> aClass, K key, K parentKey, boolean inheritContainerTags) {
|
||||
this(aClass, key, parentKey);
|
||||
this.containerData = containerData;
|
||||
if (containerData != null && inheritContainerTags) {
|
||||
this.putTags(containerData.rawTags);
|
||||
}
|
||||
}
|
||||
|
||||
public Class<? extends JsonAsset<?>> getAssetClass() {
|
||||
return this.assetClass;
|
||||
}
|
||||
|
||||
public Object getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public Object getParentKey() {
|
||||
return this.parentKey;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public AssetExtraInfo.Data getRootContainerData() {
|
||||
AssetExtraInfo.Data temp = this;
|
||||
|
||||
while (temp.containerData != null) {
|
||||
temp = temp.containerData;
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AssetExtraInfo.Data getContainerData() {
|
||||
return this.containerData;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <K> K getContainerKey(Class<? extends JsonAsset<K>> aClass) {
|
||||
if (this.containerData == null) {
|
||||
return null;
|
||||
} else {
|
||||
return (K)(this.containerData.assetClass.equals(aClass) ? this.containerData.key : this.containerData.getContainerKey(aClass));
|
||||
}
|
||||
}
|
||||
|
||||
public void putTags(@Nonnull Map<String, String[]> tags) {
|
||||
for (Entry<String, String[]> entry : tags.entrySet()) {
|
||||
String tag = entry.getKey().intern();
|
||||
this.rawTags.merge(tag, entry.getValue(), ArrayUtil::combine);
|
||||
IntSet tagIndexes = this.ensureTag(tag);
|
||||
|
||||
for (String value : entry.getValue()) {
|
||||
tagIndexes.add(AssetRegistry.getOrCreateTagIndex(value));
|
||||
this.ensureTag(value);
|
||||
this.rawTags.putIfAbsent(value, ArrayUtil.EMPTY_STRING_ARRAY);
|
||||
String valueTag = (tag + "=" + value).intern();
|
||||
this.rawTags.putIfAbsent(valueTag, ArrayUtil.EMPTY_STRING_ARRAY);
|
||||
this.ensureTag(valueTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Map<String, String[]> getRawTags() {
|
||||
return Collections.unmodifiableMap(this.rawTags);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Int2ObjectMap<IntSet> getTags() {
|
||||
return Int2ObjectMaps.unmodifiable(this.unmodifiableTagStorage);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public IntSet getExpandedTagIndexes() {
|
||||
return this.unmodifiableExpandedTagStorage;
|
||||
}
|
||||
|
||||
public IntSet getTag(int tagIndex) {
|
||||
return this.unmodifiableTagStorage.getOrDefault(tagIndex, IntSets.EMPTY_SET);
|
||||
}
|
||||
|
||||
public <K, T extends JsonAssetWithMap<K, M>, M extends AssetMap<K, T>> void addContainedAsset(Class<T> assetClass, T asset) {
|
||||
if (this.containedAssets == null) {
|
||||
this.containedAssets = new HashMap<>();
|
||||
}
|
||||
|
||||
this.containedAssets.computeIfAbsent(assetClass, k -> new ArrayList<>()).add(asset);
|
||||
}
|
||||
|
||||
public <K, T extends JsonAssetWithMap<K, M>, M extends AssetMap<K, T>> void addContainedAsset(Class<T> assetClass, RawAsset<K> rawAsset) {
|
||||
if (this.containedRawAssets == null) {
|
||||
this.containedRawAssets = new HashMap<>();
|
||||
}
|
||||
|
||||
this.containedRawAssets.computeIfAbsent(assetClass, k -> new ArrayList<>()).add(rawAsset);
|
||||
}
|
||||
|
||||
public <K> void fetchContainedAssets(K key, @Nonnull Map<Class<? extends JsonAssetWithMap>, Map<K, List<Object>>> containedAssets) {
|
||||
if (this.containedAssets != null) {
|
||||
for (Entry<Class<? extends JsonAssetWithMap>, List<Object>> entry : this.containedAssets.entrySet()) {
|
||||
containedAssets.computeIfAbsent(entry.getKey(), k -> new HashMap<>()).computeIfAbsent(key, k -> new ArrayList<>(3)).addAll(entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public <K> void fetchContainedRawAssets(K key, @Nonnull Map<Class<? extends JsonAssetWithMap>, Map<K, List<RawAsset<Object>>>> containedAssets) {
|
||||
if (this.containedRawAssets != null) {
|
||||
for (Entry<Class<? extends JsonAssetWithMap>, List<RawAsset<Object>>> entry : this.containedRawAssets.entrySet()) {
|
||||
containedAssets.computeIfAbsent(entry.getKey(), k -> new HashMap<>()).computeIfAbsent(key, k -> new ArrayList<>(3)).addAll(entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public <K, T extends JsonAssetWithMap<K, M>, M extends AssetMap<K, T>> boolean containsAsset(Class<T> tClass, K key) {
|
||||
if (this.containedAssets != null) {
|
||||
List<T> assets = (List<T>)this.containedAssets.get(tClass);
|
||||
if (assets != null) {
|
||||
Function<T, K> keyFunction = AssetRegistry.<K, T, M>getAssetStore(tClass).getKeyFunction();
|
||||
|
||||
for (T asset : assets) {
|
||||
if (key.equals(keyFunction.apply(asset))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.containedRawAssets != null) {
|
||||
List<RawAsset<T>> rawAssets = (List<RawAsset<T>>)this.containedRawAssets.get(tClass);
|
||||
if (rawAssets != null) {
|
||||
for (RawAsset<T> rawAsset : rawAssets) {
|
||||
if (key.equals(rawAsset.getKey())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void loadContainedAssets(boolean reloading) {
|
||||
if (this.containedAssets != null) {
|
||||
for (Entry<Class<? extends JsonAssetWithMap>, List<Object>> entry : this.containedAssets.entrySet()) {
|
||||
AssetRegistry.getAssetStore(entry.getKey()).loadAssets("Hytale:Hytale", entry.getValue(), AssetUpdateQuery.DEFAULT, reloading);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.containedRawAssets != null) {
|
||||
for (Entry<Class<? extends JsonAssetWithMap>, List<RawAsset<Object>>> entry : this.containedRawAssets.entrySet()) {
|
||||
AssetRegistry.getAssetStore(entry.getKey()).loadBuffersWithKeys("Hytale:Hytale", entry.getValue(), AssetUpdateQuery.DEFAULT, reloading);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private IntSet ensureTag(@Nonnull String tag) {
|
||||
int idx = AssetRegistry.getOrCreateTagIndex(tag);
|
||||
this.expandedTagStorage.add(idx);
|
||||
return this.tagStorage.computeIfAbsent(idx, k -> {
|
||||
IntSet set = new IntOpenHashSet(3);
|
||||
this.unmodifiableTagStorage.put(k, IntSets.unmodifiable(set));
|
||||
return set;
|
||||
});
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Data{containedAssets="
|
||||
+ this.containedRawAssets
|
||||
+ ", rawTags="
|
||||
+ this.rawTags
|
||||
+ ", parent="
|
||||
+ this.containerData
|
||||
+ ", assetClass="
|
||||
+ this.assetClass
|
||||
+ ", key="
|
||||
+ this.key
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
4
generated/com/hypixel/hytale/assetstore/AssetHolder.java
Normal file
4
generated/com/hypixel/hytale/assetstore/AssetHolder.java
Normal file
@@ -0,0 +1,4 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
public interface AssetHolder<K> {
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
import com.hypixel.hytale.codec.schema.SchemaContext;
|
||||
import com.hypixel.hytale.codec.schema.config.Schema;
|
||||
import com.hypixel.hytale.codec.validation.ValidationResults;
|
||||
import com.hypixel.hytale.codec.validation.Validator;
|
||||
import java.util.function.Supplier;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class AssetKeyValidator<K> implements Validator<K> {
|
||||
private final Supplier<AssetStore<K, ?, ?>> store;
|
||||
|
||||
public AssetKeyValidator(Supplier<AssetStore<K, ?, ?>> store) {
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
public AssetStore<K, ?, ?> getStore() {
|
||||
return this.store.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(K k, @Nonnull ValidationResults results) {
|
||||
this.store.get().validate(k, results, results.getExtraInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateSchema(SchemaContext context, @Nonnull Schema target) {
|
||||
target.setHytaleAssetRef(this.store.get().getAssetClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
58
generated/com/hypixel/hytale/assetstore/AssetLoadResult.java
Normal file
58
generated/com/hypixel/hytale/assetstore/AssetLoadResult.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class AssetLoadResult<K, T> {
|
||||
private final Map<K, T> loadedAssets;
|
||||
private final Map<K, Path> loadedKeyToPathMap;
|
||||
private final Set<K> failedToLoadKeys;
|
||||
private final Set<Path> failedToLoadPaths;
|
||||
private final Map<Class<? extends JsonAssetWithMap>, AssetLoadResult> childAssetResults;
|
||||
|
||||
public AssetLoadResult(
|
||||
Map<K, T> loadedAssets,
|
||||
Map<K, Path> loadedKeyToPathMap,
|
||||
Set<K> failedToLoadKeys,
|
||||
Set<Path> failedToLoadPaths,
|
||||
Map<Class<? extends JsonAssetWithMap>, AssetLoadResult> childAssetResults
|
||||
) {
|
||||
this.loadedAssets = loadedAssets;
|
||||
this.loadedKeyToPathMap = loadedKeyToPathMap;
|
||||
this.failedToLoadKeys = failedToLoadKeys;
|
||||
this.failedToLoadPaths = failedToLoadPaths;
|
||||
this.childAssetResults = childAssetResults;
|
||||
}
|
||||
|
||||
public Map<K, T> getLoadedAssets() {
|
||||
return this.loadedAssets;
|
||||
}
|
||||
|
||||
public Map<K, Path> getLoadedKeyToPathMap() {
|
||||
return this.loadedKeyToPathMap;
|
||||
}
|
||||
|
||||
public Set<K> getFailedToLoadKeys() {
|
||||
return this.failedToLoadKeys;
|
||||
}
|
||||
|
||||
public Set<Path> getFailedToLoadPaths() {
|
||||
return this.failedToLoadPaths;
|
||||
}
|
||||
|
||||
public boolean hasFailed() {
|
||||
if (this.failedToLoadKeys.isEmpty() && this.failedToLoadPaths.isEmpty()) {
|
||||
for (AssetLoadResult result : this.childAssetResults.values()) {
|
||||
if (result.hasFailed()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
58
generated/com/hypixel/hytale/assetstore/AssetMap.java
Normal file
58
generated/com/hypixel/hytale/assetstore/AssetMap.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
import com.hypixel.hytale.assetstore.codec.AssetCodec;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public abstract class AssetMap<K, T extends JsonAsset<K>> {
|
||||
public AssetMap() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public abstract T getAsset(K var1);
|
||||
|
||||
@Nullable
|
||||
public abstract T getAsset(@Nonnull String var1, K var2);
|
||||
|
||||
@Nullable
|
||||
public abstract Path getPath(K var1);
|
||||
|
||||
@Nullable
|
||||
public abstract String getAssetPack(K var1);
|
||||
|
||||
public abstract Set<K> getKeys(Path var1);
|
||||
|
||||
public abstract Set<K> getChildren(K var1);
|
||||
|
||||
public abstract int getAssetCount();
|
||||
|
||||
public abstract Map<K, T> getAssetMap();
|
||||
|
||||
public abstract Map<K, Path> getPathMap(@Nonnull String var1);
|
||||
|
||||
public abstract Set<K> getKeysForTag(int var1);
|
||||
|
||||
public abstract IntSet getTagIndexes();
|
||||
|
||||
public abstract int getTagCount();
|
||||
|
||||
protected abstract void clear();
|
||||
|
||||
protected abstract void putAll(@Nonnull String var1, AssetCodec<K, T> var2, Map<K, T> var3, Map<K, Path> var4, Map<K, Set<K>> var5);
|
||||
|
||||
protected abstract Set<K> remove(Set<K> var1);
|
||||
|
||||
protected abstract Set<K> remove(@Nonnull String var1, Set<K> var2, List<Entry<String, Object>> var3);
|
||||
|
||||
public boolean requireReplaceOnRemove() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract Set<K> getKeysForPack(@Nonnull String var1);
|
||||
}
|
||||
79
generated/com/hypixel/hytale/assetstore/AssetPack.java
Normal file
79
generated/com/hypixel/hytale/assetstore/AssetPack.java
Normal file
@@ -0,0 +1,79 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
import com.hypixel.hytale.common.plugin.PluginManifest;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.Path;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class AssetPack {
|
||||
@Nonnull
|
||||
private final String name;
|
||||
@Nonnull
|
||||
private final Path root;
|
||||
@Nullable
|
||||
private final FileSystem fileSystem;
|
||||
private final boolean isImmutable;
|
||||
private final PluginManifest manifest;
|
||||
private final Path packLocation;
|
||||
|
||||
public AssetPack(Path packLocation, @Nonnull String name, @Nonnull Path root, @Nullable FileSystem fileSystem, boolean isImmutable, PluginManifest manifest) {
|
||||
this.name = name;
|
||||
this.root = root;
|
||||
this.fileSystem = fileSystem;
|
||||
this.isImmutable = isImmutable;
|
||||
this.manifest = manifest;
|
||||
this.packLocation = packLocation;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Path getRoot() {
|
||||
return this.root;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FileSystem getFileSystem() {
|
||||
return this.fileSystem;
|
||||
}
|
||||
|
||||
public PluginManifest getManifest() {
|
||||
return this.manifest;
|
||||
}
|
||||
|
||||
public boolean isImmutable() {
|
||||
return this.isImmutable;
|
||||
}
|
||||
|
||||
public Path getPackLocation() {
|
||||
return this.packLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
AssetPack assetPack = (AssetPack)o;
|
||||
return !this.name.equals(assetPack.name) ? false : this.root.equals(assetPack.root);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.name.hashCode();
|
||||
return 31 * result + this.root.hashCode();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AssetPack{name='" + this.name + "', root=" + this.root + ", fileSystem=" + this.fileSystem + "}";
|
||||
}
|
||||
}
|
||||
39
generated/com/hypixel/hytale/assetstore/AssetReferences.java
Normal file
39
generated/com/hypixel/hytale/assetstore/AssetReferences.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class AssetReferences<CK, C extends JsonAssetWithMap<CK, ?>> {
|
||||
private final Class<C> parentAssetClass;
|
||||
private final Set<CK> parentKeys;
|
||||
|
||||
public AssetReferences(Class<C> parentAssetClass, Set<CK> parentKeys) {
|
||||
this.parentAssetClass = parentAssetClass;
|
||||
this.parentKeys = parentKeys;
|
||||
}
|
||||
|
||||
public Class<C> getParentAssetClass() {
|
||||
return this.parentAssetClass;
|
||||
}
|
||||
|
||||
public Set<CK> getParentKeys() {
|
||||
return this.parentKeys;
|
||||
}
|
||||
|
||||
public <T extends JsonAssetWithMap<K, ?>, K> void addChildAssetReferences(Class<T> tClass, K childKey) {
|
||||
Class parentAssetClass = this.parentAssetClass;
|
||||
AssetStore<CK, C, ?> assetStore = AssetRegistry.getAssetStore(parentAssetClass);
|
||||
|
||||
for (CK parentKey : this.parentKeys) {
|
||||
assetStore.addChildAssetReferences(parentKey, tClass, Collections.singleton(childKey));
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AssetReferences{parentAssetClass=" + this.parentAssetClass + ", parentKeys=" + this.parentKeys + "}";
|
||||
}
|
||||
}
|
||||
145
generated/com/hypixel/hytale/assetstore/AssetRegistry.java
Normal file
145
generated/com/hypixel/hytale/assetstore/AssetRegistry.java
Normal file
@@ -0,0 +1,145 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
import com.hypixel.hytale.assetstore.event.RegisterAssetStoreEvent;
|
||||
import com.hypixel.hytale.assetstore.event.RemoveAssetStoreEvent;
|
||||
import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;
|
||||
import com.hypixel.hytale.event.IEventDispatcher;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class AssetRegistry {
|
||||
public static final ReadWriteLock ASSET_LOCK = new ReentrantReadWriteLock();
|
||||
public static boolean HAS_INIT = false;
|
||||
public static final int TAG_NOT_FOUND = Integer.MIN_VALUE;
|
||||
private static final Map<Class<? extends JsonAssetWithMap>, AssetStore<?, ?, ?>> storeMap = new HashMap<>();
|
||||
private static final Map<Class<? extends JsonAssetWithMap>, AssetStore<?, ?, ?>> storeMapUnmodifiable = Collections.unmodifiableMap(storeMap);
|
||||
private static final AtomicInteger NEXT_TAG_INDEX = new AtomicInteger();
|
||||
private static final StampedLock TAG_LOCK = new StampedLock();
|
||||
private static final Object2IntMap<String> TAG_MAP = new Object2IntOpenHashMap<>();
|
||||
private static final Object2IntMap<String> CLIENT_TAG_MAP = new Object2IntOpenHashMap<>();
|
||||
|
||||
public AssetRegistry() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static Map<Class<? extends JsonAssetWithMap>, AssetStore<?, ?, ?>> getStoreMap() {
|
||||
return storeMapUnmodifiable;
|
||||
}
|
||||
|
||||
public static <K, T extends JsonAssetWithMap<K, M>, M extends AssetMap<K, T>> AssetStore<K, T, M> getAssetStore(Class<T> tClass) {
|
||||
return (AssetStore<K, T, M>)storeMap.get(tClass);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static <K, T extends JsonAssetWithMap<K, M>, M extends AssetMap<K, T>, S extends AssetStore<K, T, M>> S register(@Nonnull S assetStore) {
|
||||
ASSET_LOCK.writeLock().lock();
|
||||
|
||||
try {
|
||||
if (storeMap.putIfAbsent(assetStore.getAssetClass(), assetStore) != null) {
|
||||
throw new IllegalArgumentException("Asset Store already exists for " + assetStore.getAssetClass());
|
||||
}
|
||||
} finally {
|
||||
ASSET_LOCK.writeLock().unlock();
|
||||
}
|
||||
|
||||
IEventDispatcher<RegisterAssetStoreEvent, RegisterAssetStoreEvent> dispatch = assetStore.getEventBus().dispatchFor(RegisterAssetStoreEvent.class);
|
||||
if (dispatch.hasListener()) {
|
||||
dispatch.dispatch(new RegisterAssetStoreEvent(assetStore));
|
||||
}
|
||||
|
||||
return assetStore;
|
||||
}
|
||||
|
||||
public static <K, T extends JsonAssetWithMap<K, M>, M extends AssetMap<K, T>, S extends AssetStore<K, T, M>> void unregister(@Nonnull S assetStore) {
|
||||
ASSET_LOCK.writeLock().lock();
|
||||
|
||||
try {
|
||||
storeMap.remove(assetStore.getAssetClass());
|
||||
} finally {
|
||||
ASSET_LOCK.writeLock().unlock();
|
||||
}
|
||||
|
||||
IEventDispatcher<RemoveAssetStoreEvent, RemoveAssetStoreEvent> dispatch = assetStore.getEventBus().dispatchFor(RemoveAssetStoreEvent.class);
|
||||
if (dispatch.hasListener()) {
|
||||
dispatch.dispatch(new RemoveAssetStoreEvent(assetStore));
|
||||
}
|
||||
}
|
||||
|
||||
public static int getTagIndex(@Nonnull String tag) {
|
||||
if (tag == null) {
|
||||
throw new IllegalArgumentException("tag can't be null!");
|
||||
} else {
|
||||
long stamp = TAG_LOCK.readLock();
|
||||
|
||||
int var3;
|
||||
try {
|
||||
var3 = TAG_MAP.getInt(tag);
|
||||
} finally {
|
||||
TAG_LOCK.unlockRead(stamp);
|
||||
}
|
||||
|
||||
return var3;
|
||||
}
|
||||
}
|
||||
|
||||
public static int getOrCreateTagIndex(@Nonnull String tag) {
|
||||
if (tag == null) {
|
||||
throw new IllegalArgumentException("tag can't be null!");
|
||||
} else {
|
||||
long stamp = TAG_LOCK.writeLock();
|
||||
|
||||
int var3;
|
||||
try {
|
||||
var3 = TAG_MAP.computeIfAbsent(tag.intern(), k -> NEXT_TAG_INDEX.getAndIncrement());
|
||||
} finally {
|
||||
TAG_LOCK.unlockWrite(stamp);
|
||||
}
|
||||
|
||||
return var3;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean registerClientTag(@Nonnull String tag) {
|
||||
if (tag == null) {
|
||||
throw new IllegalArgumentException("tag can't be null!");
|
||||
} else {
|
||||
long stamp = TAG_LOCK.writeLock();
|
||||
|
||||
boolean var3;
|
||||
try {
|
||||
var3 = CLIENT_TAG_MAP.put(tag, TAG_MAP.computeIfAbsent(tag, k -> NEXT_TAG_INDEX.getAndIncrement())) == Integer.MIN_VALUE;
|
||||
} finally {
|
||||
TAG_LOCK.unlockWrite(stamp);
|
||||
}
|
||||
|
||||
return var3;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static Object2IntMap<String> getClientTags() {
|
||||
long stamp = TAG_LOCK.readLock();
|
||||
|
||||
Object2IntOpenHashMap var2;
|
||||
try {
|
||||
var2 = new Object2IntOpenHashMap<>(CLIENT_TAG_MAP);
|
||||
} finally {
|
||||
TAG_LOCK.unlockRead(stamp);
|
||||
}
|
||||
|
||||
return var2;
|
||||
}
|
||||
|
||||
static {
|
||||
TAG_MAP.defaultReturnValue(Integer.MIN_VALUE);
|
||||
CLIENT_TAG_MAP.defaultReturnValue(Integer.MIN_VALUE);
|
||||
}
|
||||
}
|
||||
1761
generated/com/hypixel/hytale/assetstore/AssetStore.java
Normal file
1761
generated/com/hypixel/hytale/assetstore/AssetStore.java
Normal file
File diff suppressed because it is too large
Load Diff
178
generated/com/hypixel/hytale/assetstore/AssetUpdateQuery.java
Normal file
178
generated/com/hypixel/hytale/assetstore/AssetUpdateQuery.java
Normal file
@@ -0,0 +1,178 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class AssetUpdateQuery {
|
||||
public static final AssetUpdateQuery DEFAULT = new AssetUpdateQuery(AssetUpdateQuery.RebuildCache.DEFAULT);
|
||||
public static final AssetUpdateQuery DEFAULT_NO_REBUILD = new AssetUpdateQuery(AssetUpdateQuery.RebuildCache.NO_REBUILD);
|
||||
private final boolean disableAssetCompare;
|
||||
private final AssetUpdateQuery.RebuildCache rebuildCache;
|
||||
|
||||
public AssetUpdateQuery(boolean disableAssetCompare, AssetUpdateQuery.RebuildCache rebuildCache) {
|
||||
this.disableAssetCompare = disableAssetCompare;
|
||||
this.rebuildCache = rebuildCache;
|
||||
}
|
||||
|
||||
public AssetUpdateQuery(AssetUpdateQuery.RebuildCache rebuildCache) {
|
||||
this(AssetStore.DISABLE_ASSET_COMPARE, rebuildCache);
|
||||
}
|
||||
|
||||
public boolean isDisableAssetCompare() {
|
||||
return this.disableAssetCompare;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public AssetUpdateQuery.RebuildCache getRebuildCache() {
|
||||
return this.rebuildCache;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AssetUpdateQuery{rebuildCache=" + this.rebuildCache + "}";
|
||||
}
|
||||
|
||||
public static class RebuildCache {
|
||||
public static final AssetUpdateQuery.RebuildCache DEFAULT = new AssetUpdateQuery.RebuildCache(true, true, true, true, true, true);
|
||||
public static final AssetUpdateQuery.RebuildCache NO_REBUILD = new AssetUpdateQuery.RebuildCache(false, false, false, false, false, false);
|
||||
private final boolean blockTextures;
|
||||
private final boolean models;
|
||||
private final boolean modelTextures;
|
||||
private final boolean mapGeometry;
|
||||
private final boolean itemIcons;
|
||||
private final boolean commonAssetsRebuild;
|
||||
|
||||
public RebuildCache(boolean blockTextures, boolean models, boolean modelTextures, boolean mapGeometry, boolean itemIcons, boolean commonAssetsRebuild) {
|
||||
this.blockTextures = blockTextures;
|
||||
this.models = models;
|
||||
this.modelTextures = modelTextures;
|
||||
this.mapGeometry = mapGeometry;
|
||||
this.itemIcons = itemIcons;
|
||||
this.commonAssetsRebuild = commonAssetsRebuild;
|
||||
}
|
||||
|
||||
public boolean isBlockTextures() {
|
||||
return this.blockTextures;
|
||||
}
|
||||
|
||||
public boolean isModels() {
|
||||
return this.models;
|
||||
}
|
||||
|
||||
public boolean isModelTextures() {
|
||||
return this.modelTextures;
|
||||
}
|
||||
|
||||
public boolean isMapGeometry() {
|
||||
return this.mapGeometry;
|
||||
}
|
||||
|
||||
public boolean isItemIcons() {
|
||||
return this.itemIcons;
|
||||
}
|
||||
|
||||
public boolean isCommonAssetsRebuild() {
|
||||
return this.commonAssetsRebuild;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public AssetUpdateQuery.RebuildCacheBuilder toBuilder() {
|
||||
return new AssetUpdateQuery.RebuildCacheBuilder(
|
||||
this.blockTextures, this.models, this.modelTextures, this.mapGeometry, this.itemIcons, this.commonAssetsRebuild
|
||||
);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static AssetUpdateQuery.RebuildCacheBuilder builder() {
|
||||
return new AssetUpdateQuery.RebuildCacheBuilder();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RebuildCache{blockTextures="
|
||||
+ this.blockTextures
|
||||
+ ", models="
|
||||
+ this.models
|
||||
+ ", modelTextures="
|
||||
+ this.modelTextures
|
||||
+ ", mapGeometry="
|
||||
+ this.mapGeometry
|
||||
+ ", icons="
|
||||
+ this.itemIcons
|
||||
+ ", commonAssetsRebuild="
|
||||
+ this.commonAssetsRebuild
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
||||
public static class RebuildCacheBuilder {
|
||||
private boolean blockTextures;
|
||||
private boolean models;
|
||||
private boolean modelTextures;
|
||||
private boolean mapGeometry;
|
||||
private boolean itemIcons;
|
||||
private boolean commonAssetsRebuild;
|
||||
|
||||
RebuildCacheBuilder() {
|
||||
}
|
||||
|
||||
RebuildCacheBuilder(boolean blockTextures, boolean models, boolean modelTextures, boolean mapGeometry, boolean itemIcons, boolean commonAssetsRebuild) {
|
||||
this.blockTextures = blockTextures;
|
||||
this.models = models;
|
||||
this.modelTextures = modelTextures;
|
||||
this.mapGeometry = mapGeometry;
|
||||
this.itemIcons = itemIcons;
|
||||
this.commonAssetsRebuild = commonAssetsRebuild;
|
||||
}
|
||||
|
||||
public void setBlockTextures(boolean blockTextures) {
|
||||
this.blockTextures = blockTextures;
|
||||
}
|
||||
|
||||
public void setModels(boolean models) {
|
||||
this.models = models;
|
||||
}
|
||||
|
||||
public void setModelTextures(boolean modelTextures) {
|
||||
this.modelTextures = modelTextures;
|
||||
}
|
||||
|
||||
public void setMapGeometry(boolean mapGeometry) {
|
||||
this.mapGeometry = mapGeometry;
|
||||
}
|
||||
|
||||
public void setItemIcons(boolean itemIcons) {
|
||||
this.itemIcons = itemIcons;
|
||||
}
|
||||
|
||||
public void setCommonAssetsRebuild(boolean commonAssetsRebuild) {
|
||||
this.commonAssetsRebuild = commonAssetsRebuild;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public AssetUpdateQuery.RebuildCache build() {
|
||||
return new AssetUpdateQuery.RebuildCache(
|
||||
this.blockTextures, this.models, this.modelTextures, this.mapGeometry, this.itemIcons, this.commonAssetsRebuild
|
||||
);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RebuildCache{blockTextures="
|
||||
+ this.blockTextures
|
||||
+ ", models="
|
||||
+ this.models
|
||||
+ ", modelTextures="
|
||||
+ this.modelTextures
|
||||
+ ", mapGeometry="
|
||||
+ this.mapGeometry
|
||||
+ ", icons="
|
||||
+ this.itemIcons
|
||||
+ ", commonAssetsRebuild="
|
||||
+ this.commonAssetsRebuild
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
import com.hypixel.hytale.codec.ExtraInfo;
|
||||
import com.hypixel.hytale.codec.validation.ValidationResults;
|
||||
import com.hypixel.hytale.logger.HytaleLogger;
|
||||
import com.hypixel.hytale.logger.backend.HytaleLoggerBackend;
|
||||
import com.hypixel.hytale.logger.util.GithubMessageUtil;
|
||||
import java.nio.file.Path;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class AssetValidationResults extends ValidationResults {
|
||||
private Set<Class<? extends JsonAsset>> disabledMissingAssetClasses;
|
||||
|
||||
public AssetValidationResults(ExtraInfo extraInfo) {
|
||||
super(extraInfo);
|
||||
}
|
||||
|
||||
public void handleMissingAsset(String field, @Nonnull Class<? extends JsonAsset> assetType, Object assetId) {
|
||||
if (this.disabledMissingAssetClasses == null || !this.disabledMissingAssetClasses.contains(assetType)) {
|
||||
throw new MissingAssetException(field, assetType, assetId);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleMissingAsset(String field, @Nonnull Class<? extends JsonAsset> assetType, Object assetId, String extra) {
|
||||
if (this.disabledMissingAssetClasses == null || !this.disabledMissingAssetClasses.contains(assetType)) {
|
||||
throw new MissingAssetException(field, assetType, assetId, extra);
|
||||
}
|
||||
}
|
||||
|
||||
public void disableMissingAssetFor(Class<? extends JsonAsset> assetType) {
|
||||
if (this.disabledMissingAssetClasses == null) {
|
||||
this.disabledMissingAssetClasses = new HashSet<>();
|
||||
}
|
||||
|
||||
this.disabledMissingAssetClasses.add(assetType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void logOrThrowValidatorExceptions(@Nonnull HytaleLogger logger, @Nonnull String msg) {
|
||||
this.logOrThrowValidatorExceptions(logger, msg, null, 0);
|
||||
}
|
||||
|
||||
public void logOrThrowValidatorExceptions(@Nonnull HytaleLogger logger, @Nonnull String msg, @Nullable Path path, int lineOffset) {
|
||||
if (GithubMessageUtil.isGithub() && this.validatorExceptions != null && !this.validatorExceptions.isEmpty()) {
|
||||
for (ValidationResults.ValidatorResultsHolder holder : this.validatorExceptions) {
|
||||
String file = "unknown";
|
||||
if (path == null && this.extraInfo instanceof AssetExtraInfo<?> assetExtraInfo) {
|
||||
path = assetExtraInfo.getAssetPath();
|
||||
}
|
||||
|
||||
if (path != null) {
|
||||
file = path.toString();
|
||||
}
|
||||
|
||||
for (ValidationResults.ValidationResult result : holder.results()) {
|
||||
HytaleLoggerBackend.rawLog(
|
||||
switch (result.result()) {
|
||||
case SUCCESS -> "";
|
||||
case WARNING -> holder.line() == -1
|
||||
? GithubMessageUtil.messageWarning(file, result.reason())
|
||||
: GithubMessageUtil.messageWarning(file, holder.line() + lineOffset, holder.column(), result.reason());
|
||||
case FAIL -> holder.line() == -1
|
||||
? GithubMessageUtil.messageError(file, result.reason())
|
||||
: GithubMessageUtil.messageError(file, holder.line() + lineOffset, holder.column(), result.reason());
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.logOrThrowValidatorExceptions(logger, msg);
|
||||
}
|
||||
}
|
||||
50
generated/com/hypixel/hytale/assetstore/DecodedAsset.java
Normal file
50
generated/com/hypixel/hytale/assetstore/DecodedAsset.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class DecodedAsset<K, T extends JsonAsset<K>> implements AssetHolder<K> {
|
||||
private final K key;
|
||||
private final T asset;
|
||||
|
||||
public DecodedAsset(K key, T asset) {
|
||||
this.key = key;
|
||||
this.asset = asset;
|
||||
}
|
||||
|
||||
public K getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public T getAsset() {
|
||||
return this.asset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
} else if (o != null && this.getClass() == o.getClass()) {
|
||||
DecodedAsset<?, ?> that = (DecodedAsset<?, ?>)o;
|
||||
if (this.key != null ? this.key.equals(that.key) : that.key == null) {
|
||||
return this.asset != null ? this.asset.equals(that.asset) : that.asset == null;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.key != null ? this.key.hashCode() : 0;
|
||||
return 31 * result + (this.asset != null ? this.asset.hashCode() : 0);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DecodedAsset{key=" + this.key + ", asset=" + this.asset + "}";
|
||||
}
|
||||
}
|
||||
5
generated/com/hypixel/hytale/assetstore/JsonAsset.java
Normal file
5
generated/com/hypixel/hytale/assetstore/JsonAsset.java
Normal file
@@ -0,0 +1,5 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
public interface JsonAsset<K> {
|
||||
K getId();
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
import com.hypixel.hytale.codec.ExtraInfo;
|
||||
import com.hypixel.hytale.codec.validation.ValidationResults;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class MissingAssetException extends RuntimeException {
|
||||
private String field;
|
||||
private Class<? extends JsonAsset> assetType;
|
||||
private Object assetId;
|
||||
|
||||
public MissingAssetException(String field, @Nonnull Class<? extends JsonAsset> assetType, Object assetId) {
|
||||
super("Missing asset '" + assetId + "' of type " + assetType.getSimpleName() + " for field '" + field + "'!");
|
||||
this.field = field;
|
||||
this.assetType = assetType;
|
||||
this.assetId = assetId;
|
||||
}
|
||||
|
||||
public MissingAssetException(String field, @Nonnull Class<? extends JsonAsset> assetType, Object assetId, String extra) {
|
||||
super("Missing asset '" + assetId + "' of type " + assetType.getSimpleName() + " for field '" + field + "'! " + extra);
|
||||
this.field = field;
|
||||
this.assetType = assetType;
|
||||
this.assetId = assetId;
|
||||
}
|
||||
|
||||
public String getField() {
|
||||
return this.field;
|
||||
}
|
||||
|
||||
public Class<? extends JsonAsset> getAssetType() {
|
||||
return this.assetType;
|
||||
}
|
||||
|
||||
public Object getAssetId() {
|
||||
return this.assetId;
|
||||
}
|
||||
|
||||
public static void handle(@Nonnull ExtraInfo extraInfo, String field, @Nonnull Class<? extends JsonAsset> assetType, Object assetId) {
|
||||
ValidationResults validationResults = extraInfo.getValidationResults();
|
||||
if (validationResults instanceof AssetValidationResults) {
|
||||
((AssetValidationResults)validationResults).handleMissingAsset(field, assetType, assetId);
|
||||
} else {
|
||||
throw new MissingAssetException(field, assetType, assetId);
|
||||
}
|
||||
}
|
||||
|
||||
public static void handle(@Nonnull ExtraInfo extraInfo, String field, @Nonnull Class<? extends JsonAsset> assetType, Object assetId, String extra) {
|
||||
ValidationResults validationResults = extraInfo.getValidationResults();
|
||||
if (validationResults instanceof AssetValidationResults) {
|
||||
((AssetValidationResults)validationResults).handleMissingAsset(field, assetType, assetId, extra);
|
||||
} else {
|
||||
throw new MissingAssetException(field, assetType, assetId, extra);
|
||||
}
|
||||
}
|
||||
}
|
||||
153
generated/com/hypixel/hytale/assetstore/RawAsset.java
Normal file
153
generated/com/hypixel/hytale/assetstore/RawAsset.java
Normal file
@@ -0,0 +1,153 @@
|
||||
package com.hypixel.hytale.assetstore;
|
||||
|
||||
import com.hypixel.hytale.assetstore.codec.ContainedAssetCodec;
|
||||
import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;
|
||||
import com.hypixel.hytale.codec.util.RawJsonReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Supplier;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNullableByDefault;
|
||||
|
||||
@ParametersAreNullableByDefault
|
||||
public class RawAsset<K> implements AssetHolder<K> {
|
||||
private final Path parentPath;
|
||||
@Nullable
|
||||
private final K key;
|
||||
private final int lineOffset;
|
||||
private final boolean parentKeyResolved;
|
||||
@Nullable
|
||||
private final K parentKey;
|
||||
@Nullable
|
||||
private final Path path;
|
||||
@Nullable
|
||||
private final char[] buffer;
|
||||
@Nullable
|
||||
private final AssetExtraInfo.Data containerData;
|
||||
@Nonnull
|
||||
private final ContainedAssetCodec.Mode containedAssetMode;
|
||||
|
||||
public RawAsset(K key, Path path) {
|
||||
this.key = key;
|
||||
this.lineOffset = 0;
|
||||
this.parentKeyResolved = false;
|
||||
this.parentKey = null;
|
||||
this.path = path;
|
||||
this.parentPath = null;
|
||||
this.buffer = null;
|
||||
this.containerData = null;
|
||||
this.containedAssetMode = ContainedAssetCodec.Mode.NONE;
|
||||
}
|
||||
|
||||
public RawAsset(
|
||||
Path parentPath,
|
||||
K key,
|
||||
K parentKey,
|
||||
int lineOffset,
|
||||
char[] buffer,
|
||||
AssetExtraInfo.Data containerData,
|
||||
@Nonnull ContainedAssetCodec.Mode containedAssetMode
|
||||
) {
|
||||
this.key = key;
|
||||
this.lineOffset = lineOffset;
|
||||
this.parentKeyResolved = true;
|
||||
this.parentKey = parentKey;
|
||||
this.path = null;
|
||||
this.parentPath = parentPath;
|
||||
this.buffer = buffer;
|
||||
this.containerData = containerData;
|
||||
this.containedAssetMode = containedAssetMode;
|
||||
}
|
||||
|
||||
private RawAsset(
|
||||
K key,
|
||||
boolean parentKeyResolved,
|
||||
K parentKey,
|
||||
Path path,
|
||||
char[] buffer,
|
||||
AssetExtraInfo.Data containerData,
|
||||
@Nonnull ContainedAssetCodec.Mode containedAssetMode
|
||||
) {
|
||||
this.key = key;
|
||||
this.lineOffset = 0;
|
||||
this.parentKeyResolved = parentKeyResolved;
|
||||
this.parentKey = parentKey;
|
||||
this.path = path;
|
||||
this.parentPath = null;
|
||||
this.buffer = buffer;
|
||||
this.containerData = containerData;
|
||||
this.containedAssetMode = containedAssetMode;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public K getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
public boolean isParentKeyResolved() {
|
||||
return this.parentKeyResolved;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public K getParentKey() {
|
||||
return this.parentKey;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Path getPath() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
public Path getParentPath() {
|
||||
return this.parentPath;
|
||||
}
|
||||
|
||||
public int getLineOffset() {
|
||||
return this.lineOffset;
|
||||
}
|
||||
|
||||
public char[] getBuffer() {
|
||||
return this.buffer;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ContainedAssetCodec.Mode getContainedAssetMode() {
|
||||
return this.containedAssetMode;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public RawJsonReader toRawJsonReader(@Nonnull Supplier<char[]> bufferSupplier) throws IOException {
|
||||
return this.path != null ? RawJsonReader.fromPath(this.path, bufferSupplier.get()) : RawJsonReader.fromBuffer(this.buffer);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public AssetExtraInfo.Data makeData(Class<? extends JsonAssetWithMap<K, ?>> aClass, K key, K parentKey) {
|
||||
boolean inheritTags = switch (this.containedAssetMode) {
|
||||
case INHERIT_ID, INHERIT_ID_AND_PARENT, INJECT_PARENT -> true;
|
||||
case NONE, GENERATE_ID -> false;
|
||||
};
|
||||
return new AssetExtraInfo.Data(this.containerData, aClass, key, parentKey, inheritTags);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public RawAsset<K> withResolveKeys(K key, K parentKey) {
|
||||
return new RawAsset<>(key, true, parentKey, this.path, this.buffer, this.containerData, this.containedAssetMode);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RawAsset{key="
|
||||
+ this.key
|
||||
+ ", parentKeyResolved="
|
||||
+ this.parentKeyResolved
|
||||
+ ", parentKey="
|
||||
+ this.parentKey
|
||||
+ ", path="
|
||||
+ this.path
|
||||
+ ", buffer.length="
|
||||
+ (this.buffer != null ? this.buffer.length : -1)
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package com.hypixel.hytale.assetstore.codec;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetExtraInfo;
|
||||
import com.hypixel.hytale.assetstore.JsonAsset;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.map.MapCodec;
|
||||
import com.hypixel.hytale.codec.schema.SchemaContext;
|
||||
import com.hypixel.hytale.codec.schema.config.ObjectSchema;
|
||||
import com.hypixel.hytale.codec.schema.config.Schema;
|
||||
import com.hypixel.hytale.codec.util.RawJsonReader;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class AssetBuilderCodec<K, T extends JsonAsset<K>> extends BuilderCodec<T> implements AssetCodec<K, T> {
|
||||
public static final KeyedCodec<Map<String, String[]>> TAGS_CODEC = new KeyedCodec<>("Tags", new MapCodec<>(Codec.STRING_ARRAY, HashMap::new));
|
||||
private static final String TAG_DOCUMENTATION = "Tags are a general way to describe an asset that can be interpreted by other systems in a way they see fit.\n\nFor example you could tag something with a **Material** tag with the values **Solid** and **Stone**, And another single tag **Ore**.\n\nTags will be expanded into a single list of tags automatically. Using the above example with **Material** and **Ore** the end result would be the following list of tags: **Ore**, **Material**, **Solid**, **Stone**, **Material=Solid** and **Material=Stone**.";
|
||||
@Nonnull
|
||||
protected final KeyedCodec<K> idCodec;
|
||||
@Nonnull
|
||||
protected final KeyedCodec<K> parentCodec;
|
||||
protected final BiConsumer<T, K> idSetter;
|
||||
protected final BiConsumer<T, AssetExtraInfo.Data> dataSetter;
|
||||
@Nonnull
|
||||
protected final Function<T, AssetExtraInfo.Data> dataGetter;
|
||||
|
||||
protected AssetBuilderCodec(@Nonnull AssetBuilderCodec.Builder<K, T> builder) {
|
||||
super(builder);
|
||||
this.idCodec = builder.idCodec;
|
||||
this.parentCodec = new KeyedCodec<>("Parent", this.idCodec.getChildCodec());
|
||||
this.idSetter = builder.idSetter;
|
||||
this.dataSetter = builder.dataSetter;
|
||||
this.dataGetter = builder.dataGetter;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public KeyedCodec<K> getKeyCodec() {
|
||||
return this.idCodec;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public KeyedCodec<K> getParentCodec() {
|
||||
return this.parentCodec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetExtraInfo.Data getData(T t) {
|
||||
return this.dataGetter.apply(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T decodeJsonAsset(@Nonnull RawJsonReader reader, @Nonnull AssetExtraInfo<K> extraInfo) throws IOException {
|
||||
return this.decodeAndInheritJsonAsset(reader, null, extraInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T decodeAndInheritJsonAsset(@Nonnull RawJsonReader reader, @Nullable T parent, @Nonnull AssetExtraInfo<K> extraInfo) throws IOException {
|
||||
T t = this.supplier.get();
|
||||
this.dataSetter.accept(t, extraInfo.getData());
|
||||
if (parent != null) {
|
||||
this.inherit(t, parent, extraInfo);
|
||||
}
|
||||
|
||||
this.decodeAndInheritJson0(reader, t, parent, extraInfo);
|
||||
this.idSetter.accept(t, extraInfo.getKey());
|
||||
this.afterDecodeAndValidate(t, extraInfo);
|
||||
return t;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public ObjectSchema toSchema(@Nonnull SchemaContext context) {
|
||||
return this.toSchema(context, this.supplier.get());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ObjectSchema toSchema(@Nonnull SchemaContext context, @Nullable T def) {
|
||||
ObjectSchema schema = super.toSchema(context, def);
|
||||
KeyedCodec<K> parent = this.getParentCodec();
|
||||
Schema parentSchema = parent.getChildCodec().toSchema(context);
|
||||
parentSchema.setMarkdownDescription(
|
||||
"When set this asset will inherit properties from the named asset.\n\nWhen inheriting from another **"
|
||||
+ this.tClass.getSimpleName()
|
||||
+ "** most properties will simply be copied from the parent asset to this asset. In the case where both child and parent provide a field the child field will simply replace the value provided by the parent, in the case of nested structures this will apply to the fields within the structure. In some cases the field may decide to act differently, for example: by merging the parent and child fields together."
|
||||
);
|
||||
Class<? super T> rootClass = this.tClass;
|
||||
|
||||
for (BuilderCodec<? super T> rootCodec = this; rootCodec.getParent() != null; rootClass = rootCodec.getInnerClass()) {
|
||||
rootCodec = rootCodec.getParent();
|
||||
}
|
||||
|
||||
parentSchema.setHytaleParent(new Schema.InheritSettings(rootClass.getSimpleName()));
|
||||
LinkedHashMap<String, Schema> props = new LinkedHashMap<>();
|
||||
props.put(parent.getKey(), parentSchema);
|
||||
props.putAll(schema.getProperties());
|
||||
schema.setProperties(props);
|
||||
return schema;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static <K, T extends JsonAsset<K>> AssetBuilderCodec.Builder<K, T> builder(
|
||||
Class<T> tClass,
|
||||
Supplier<T> supplier,
|
||||
Codec<K> idCodec,
|
||||
BiConsumer<T, K> idSetter,
|
||||
Function<T, K> idGetter,
|
||||
BiConsumer<T, AssetExtraInfo.Data> dataSetter,
|
||||
@Nonnull Function<T, AssetExtraInfo.Data> dataGetter
|
||||
) {
|
||||
return new AssetBuilderCodec.Builder<>(tClass, supplier, idCodec, idSetter, idGetter, dataSetter, dataGetter);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static <K, T extends JsonAsset<K>> AssetBuilderCodec.Builder<K, T> builder(
|
||||
Class<T> tClass,
|
||||
Supplier<T> supplier,
|
||||
BuilderCodec<? super T> parentCodec,
|
||||
Codec<K> idCodec,
|
||||
BiConsumer<T, K> idSetter,
|
||||
Function<T, K> idGetter,
|
||||
BiConsumer<T, AssetExtraInfo.Data> dataSetter,
|
||||
@Nonnull Function<T, AssetExtraInfo.Data> dataGetter
|
||||
) {
|
||||
return new AssetBuilderCodec.Builder<>(tClass, supplier, parentCodec, idCodec, idSetter, idGetter, dataSetter, dataGetter);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static <K, T extends JsonAsset<K>> AssetBuilderCodec<K, T> wrap(
|
||||
@Nonnull BuilderCodec<T> codec,
|
||||
Codec<K> idCodec,
|
||||
BiConsumer<T, K> idSetter,
|
||||
Function<T, K> idGetter,
|
||||
BiConsumer<T, AssetExtraInfo.Data> dataSetter,
|
||||
@Nonnull Function<T, AssetExtraInfo.Data> dataGetter
|
||||
) {
|
||||
return builder(codec.getInnerClass(), codec.getSupplier(), codec, idCodec, idSetter, idGetter, dataSetter, dataGetter)
|
||||
.documentation(codec.getDocumentation())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static class Builder<K, T extends JsonAsset<K>> extends BuilderCodec.BuilderBase<T, AssetBuilderCodec.Builder<K, T>> {
|
||||
@Nonnull
|
||||
protected final KeyedCodec<K> idCodec;
|
||||
protected final BiConsumer<T, K> idSetter;
|
||||
protected final BiConsumer<T, AssetExtraInfo.Data> dataSetter;
|
||||
@Nonnull
|
||||
protected final Function<T, AssetExtraInfo.Data> dataGetter;
|
||||
|
||||
public Builder(
|
||||
Class<T> tClass,
|
||||
Supplier<T> supplier,
|
||||
Codec<K> idCodec,
|
||||
BiConsumer<T, K> idSetter,
|
||||
Function<T, K> idGetter,
|
||||
BiConsumer<T, AssetExtraInfo.Data> dataSetter,
|
||||
@Nonnull Function<T, AssetExtraInfo.Data> dataGetter
|
||||
) {
|
||||
super(tClass, supplier);
|
||||
this.idCodec = new KeyedCodec<>("Id", idCodec);
|
||||
this.idSetter = idSetter;
|
||||
this.dataSetter = dataSetter;
|
||||
this.dataGetter = dataGetter;
|
||||
this.<Map<String, String[]>>appendInherited(AssetBuilderCodec.TAGS_CODEC, (t, tags) -> dataGetter.apply(t).putTags(tags), t -> {
|
||||
AssetExtraInfo.Data data = dataGetter.apply(t);
|
||||
return data != null ? data.getRawTags() : null;
|
||||
}, (t, parent) -> {
|
||||
AssetExtraInfo.Data data = dataGetter.apply(t);
|
||||
AssetExtraInfo.Data parentData = dataGetter.apply(parent);
|
||||
if (data != null && parentData != null) {
|
||||
data.putTags(parentData.getRawTags());
|
||||
}
|
||||
})
|
||||
.documentation(
|
||||
"Tags are a general way to describe an asset that can be interpreted by other systems in a way they see fit.\n\nFor example you could tag something with a **Material** tag with the values **Solid** and **Stone**, And another single tag **Ore**.\n\nTags will be expanded into a single list of tags automatically. Using the above example with **Material** and **Ore** the end result would be the following list of tags: **Ore**, **Material**, **Solid**, **Stone**, **Material=Solid** and **Material=Stone**."
|
||||
)
|
||||
.add();
|
||||
}
|
||||
|
||||
public Builder(
|
||||
Class<T> tClass,
|
||||
Supplier<T> supplier,
|
||||
BuilderCodec<? super T> parentCodec,
|
||||
Codec<K> idCodec,
|
||||
BiConsumer<T, K> idSetter,
|
||||
Function<T, K> idGetter,
|
||||
BiConsumer<T, AssetExtraInfo.Data> dataSetter,
|
||||
@Nonnull Function<T, AssetExtraInfo.Data> dataGetter
|
||||
) {
|
||||
super(tClass, supplier, parentCodec);
|
||||
this.idCodec = new KeyedCodec<>("Id", idCodec);
|
||||
this.idSetter = idSetter;
|
||||
this.dataSetter = dataSetter;
|
||||
this.dataGetter = dataGetter;
|
||||
this.<Map<String, String[]>>appendInherited(AssetBuilderCodec.TAGS_CODEC, (t, tags) -> dataGetter.apply(t).putTags(tags), t -> {
|
||||
AssetExtraInfo.Data data = dataGetter.apply(t);
|
||||
return data != null ? data.getRawTags() : null;
|
||||
}, (t, parent) -> {
|
||||
AssetExtraInfo.Data data = dataGetter.apply(t);
|
||||
AssetExtraInfo.Data parentData = dataGetter.apply(parent);
|
||||
if (data != null && parentData != null) {
|
||||
data.putTags(parentData.getRawTags());
|
||||
}
|
||||
})
|
||||
.documentation(
|
||||
"Tags are a general way to describe an asset that can be interpreted by other systems in a way they see fit.\n\nFor example you could tag something with a **Material** tag with the values **Solid** and **Stone**, And another single tag **Ore**.\n\nTags will be expanded into a single list of tags automatically. Using the above example with **Material** and **Ore** the end result would be the following list of tags: **Ore**, **Material**, **Solid**, **Stone**, **Material=Solid** and **Material=Stone**."
|
||||
)
|
||||
.add();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public AssetBuilderCodec<K, T> build() {
|
||||
return new AssetBuilderCodec<>(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.hypixel.hytale.assetstore.codec;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetExtraInfo;
|
||||
import com.hypixel.hytale.assetstore.JsonAsset;
|
||||
import com.hypixel.hytale.codec.InheritCodec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.util.RawJsonReader;
|
||||
import com.hypixel.hytale.codec.validation.ValidatableCodec;
|
||||
import java.io.IOException;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface AssetCodec<K, T extends JsonAsset<K>> extends InheritCodec<T>, ValidatableCodec<T> {
|
||||
KeyedCodec<K> getKeyCodec();
|
||||
|
||||
KeyedCodec<K> getParentCodec();
|
||||
|
||||
@Nullable
|
||||
AssetExtraInfo.Data getData(T var1);
|
||||
|
||||
T decodeJsonAsset(RawJsonReader var1, AssetExtraInfo<K> var2) throws IOException;
|
||||
|
||||
T decodeAndInheritJsonAsset(RawJsonReader var1, T var2, AssetExtraInfo<K> var3) throws IOException;
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
package com.hypixel.hytale.assetstore.codec;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetExtraInfo;
|
||||
import com.hypixel.hytale.assetstore.JsonAsset;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.ExtraInfo;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.exception.CodecException;
|
||||
import com.hypixel.hytale.codec.lookup.ACodecMapCodec;
|
||||
import com.hypixel.hytale.codec.lookup.Priority;
|
||||
import com.hypixel.hytale.codec.lookup.StringCodecMapCodec;
|
||||
import com.hypixel.hytale.codec.schema.SchemaContext;
|
||||
import com.hypixel.hytale.codec.schema.config.ObjectSchema;
|
||||
import com.hypixel.hytale.codec.schema.config.Schema;
|
||||
import com.hypixel.hytale.codec.util.RawJsonReader;
|
||||
import java.io.IOException;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import org.bson.BsonDocument;
|
||||
import org.bson.BsonValue;
|
||||
|
||||
public class AssetCodecMapCodec<K, T extends JsonAsset<K>> extends StringCodecMapCodec<T, AssetBuilderCodec<K, T>> implements AssetCodec<K, T> {
|
||||
@Nonnull
|
||||
protected final KeyedCodec<K> idCodec;
|
||||
@Nonnull
|
||||
protected final KeyedCodec<K> parentCodec;
|
||||
protected final BiConsumer<T, K> idSetter;
|
||||
protected final Function<T, K> idGetter;
|
||||
protected final BiConsumer<T, AssetExtraInfo.Data> dataSetter;
|
||||
protected final Function<T, AssetExtraInfo.Data> dataGetter;
|
||||
|
||||
public AssetCodecMapCodec(
|
||||
Codec<K> idCodec,
|
||||
BiConsumer<T, K> idSetter,
|
||||
Function<T, K> idGetter,
|
||||
BiConsumer<T, AssetExtraInfo.Data> dataSetter,
|
||||
Function<T, AssetExtraInfo.Data> dataGetter
|
||||
) {
|
||||
super("Type");
|
||||
this.idCodec = new KeyedCodec<>("Id", idCodec);
|
||||
this.parentCodec = new KeyedCodec<>("Parent", idCodec);
|
||||
this.idSetter = idSetter;
|
||||
this.idGetter = idGetter;
|
||||
this.dataSetter = dataSetter;
|
||||
this.dataGetter = dataGetter;
|
||||
}
|
||||
|
||||
public AssetCodecMapCodec(
|
||||
String key,
|
||||
Codec<K> idCodec,
|
||||
BiConsumer<T, K> idSetter,
|
||||
Function<T, K> idGetter,
|
||||
BiConsumer<T, AssetExtraInfo.Data> dataSetter,
|
||||
Function<T, AssetExtraInfo.Data> dataGetter
|
||||
) {
|
||||
super(key);
|
||||
this.idCodec = new KeyedCodec<>("Id", idCodec);
|
||||
this.parentCodec = new KeyedCodec<>("Parent", idCodec);
|
||||
this.idSetter = idSetter;
|
||||
this.idGetter = idGetter;
|
||||
this.dataSetter = dataSetter;
|
||||
this.dataGetter = dataGetter;
|
||||
}
|
||||
|
||||
public AssetCodecMapCodec(
|
||||
Codec<K> idCodec,
|
||||
BiConsumer<T, K> idSetter,
|
||||
Function<T, K> idGetter,
|
||||
BiConsumer<T, AssetExtraInfo.Data> dataSetter,
|
||||
Function<T, AssetExtraInfo.Data> dataGetter,
|
||||
boolean allowDefault
|
||||
) {
|
||||
super("Type", allowDefault);
|
||||
this.idCodec = new KeyedCodec<>("Id", idCodec);
|
||||
this.parentCodec = new KeyedCodec<>("Parent", idCodec);
|
||||
this.idSetter = idSetter;
|
||||
this.idGetter = idGetter;
|
||||
this.dataSetter = dataSetter;
|
||||
this.dataGetter = dataGetter;
|
||||
}
|
||||
|
||||
public AssetCodecMapCodec(
|
||||
String key,
|
||||
Codec<K> idCodec,
|
||||
BiConsumer<T, K> idSetter,
|
||||
Function<T, K> idGetter,
|
||||
BiConsumer<T, AssetExtraInfo.Data> dataSetter,
|
||||
Function<T, AssetExtraInfo.Data> dataGetter,
|
||||
boolean allowDefault
|
||||
) {
|
||||
super(key, allowDefault);
|
||||
this.idCodec = new KeyedCodec<>("Id", idCodec);
|
||||
this.parentCodec = new KeyedCodec<>("Parent", idCodec);
|
||||
this.idSetter = idSetter;
|
||||
this.idGetter = idGetter;
|
||||
this.dataSetter = dataSetter;
|
||||
this.dataGetter = dataGetter;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public KeyedCodec<K> getKeyCodec() {
|
||||
return this.idCodec;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public KeyedCodec<K> getParentCodec() {
|
||||
return this.parentCodec;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetExtraInfo.Data getData(T t) {
|
||||
return this.dataGetter.apply(t);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public AssetCodecMapCodec<K, T> register(@Nonnull String id, Class<? extends T> aClass, BuilderCodec<? extends T> codec) {
|
||||
return this.register(Priority.NORMAL, id, aClass, codec);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public AssetCodecMapCodec<K, T> register(@Nonnull Priority priority, @Nonnull String id, Class<? extends T> aClass, BuilderCodec<? extends T> codec) {
|
||||
AssetBuilderCodec<K, T> assetCodec = AssetBuilderCodec.wrap(
|
||||
(BuilderCodec<T>)codec, this.idCodec.getChildCodec(), this.idSetter, this.idGetter, this.dataSetter, this.dataGetter
|
||||
);
|
||||
super.register(priority, id, aClass, assetCodec);
|
||||
return this;
|
||||
}
|
||||
|
||||
public T decodeAndInherit(@Nonnull BsonDocument document, T parent, ExtraInfo extraInfo) {
|
||||
BsonValue id = document.get(this.key);
|
||||
AssetBuilderCodec<K, T> codec = this.idToCodec.get(id == null ? null : id.asString().getValue());
|
||||
if (codec == null) {
|
||||
AssetBuilderCodec<K, T> defaultCodec = this.getDefaultCodec();
|
||||
if (defaultCodec == null) {
|
||||
throw new ACodecMapCodec.UnknownIdException("No codec registered with for '" + this.key + "': " + id);
|
||||
} else {
|
||||
return defaultCodec.decodeAndInherit(document, parent, extraInfo);
|
||||
}
|
||||
} else {
|
||||
return codec.decodeAndInherit(document, parent, extraInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public void decodeAndInherit(@Nonnull BsonDocument document, T t, T parent, ExtraInfo extraInfo) {
|
||||
BsonValue id = document.get(this.key);
|
||||
AssetBuilderCodec<K, T> codec = this.idToCodec.get(id == null ? null : id.asString().getValue());
|
||||
if (codec == null) {
|
||||
AssetBuilderCodec<K, T> defaultCodec = this.getDefaultCodec();
|
||||
if (defaultCodec == null) {
|
||||
throw new ACodecMapCodec.UnknownIdException("No codec registered with for '" + this.key + "': " + id);
|
||||
} else {
|
||||
defaultCodec.decodeAndInherit(document, t, parent, extraInfo);
|
||||
}
|
||||
} else {
|
||||
codec.decodeAndInherit(document, t, parent, extraInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public T decodeAndInheritJson(@Nonnull RawJsonReader reader, @Nullable T parent, @Nonnull ExtraInfo extraInfo) throws IOException {
|
||||
reader.mark();
|
||||
String id = null;
|
||||
if (RawJsonReader.seekToKey(reader, this.key)) {
|
||||
id = reader.readString();
|
||||
} else if (parent != null) {
|
||||
id = this.getIdFor((Class<? extends T>)parent.getClass());
|
||||
}
|
||||
|
||||
reader.reset();
|
||||
extraInfo.ignoreUnusedKey(this.key);
|
||||
|
||||
JsonAsset var7;
|
||||
try {
|
||||
AssetBuilderCodec<K, T> codec = id == null ? null : this.idToCodec.get(id);
|
||||
if (codec != null) {
|
||||
return codec.decodeAndInheritJson(reader, parent, extraInfo);
|
||||
}
|
||||
|
||||
AssetBuilderCodec<K, T> defaultCodec = this.getDefaultCodec();
|
||||
if (defaultCodec == null) {
|
||||
throw new ACodecMapCodec.UnknownIdException("No codec registered with for '" + this.key + "': " + id);
|
||||
}
|
||||
|
||||
var7 = defaultCodec.decodeAndInheritJson(reader, parent, extraInfo);
|
||||
} finally {
|
||||
extraInfo.popIgnoredUnusedKey();
|
||||
}
|
||||
|
||||
return (T)var7;
|
||||
}
|
||||
|
||||
public void decodeAndInheritJson(@Nonnull RawJsonReader reader, T t, @Nullable T parent, @Nonnull ExtraInfo extraInfo) throws IOException {
|
||||
reader.mark();
|
||||
String id = null;
|
||||
if (RawJsonReader.seekToKey(reader, this.key)) {
|
||||
id = reader.readString();
|
||||
} else if (parent != null) {
|
||||
id = this.getIdFor((Class<? extends T>)parent.getClass());
|
||||
}
|
||||
|
||||
reader.reset();
|
||||
extraInfo.ignoreUnusedKey(this.key);
|
||||
|
||||
try {
|
||||
AssetBuilderCodec<K, T> codec = id == null ? null : this.idToCodec.get(id);
|
||||
if (codec != null) {
|
||||
codec.decodeAndInheritJson(reader, t, parent, extraInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
AssetBuilderCodec<K, T> defaultCodec = this.getDefaultCodec();
|
||||
if (defaultCodec == null) {
|
||||
throw new ACodecMapCodec.UnknownIdException("No codec registered with for '" + this.key + "': " + id);
|
||||
}
|
||||
|
||||
defaultCodec.decodeAndInheritJson(reader, t, parent, extraInfo);
|
||||
} finally {
|
||||
extraInfo.popIgnoredUnusedKey();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T decodeJsonAsset(@Nonnull RawJsonReader reader, @Nonnull AssetExtraInfo<K> extraInfo) throws IOException {
|
||||
return this.decodeAndInheritJsonAsset(reader, null, extraInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T decodeAndInheritJsonAsset(@Nonnull RawJsonReader reader, @Nullable T parent, @Nonnull AssetExtraInfo<K> extraInfo) throws IOException {
|
||||
reader.mark();
|
||||
String id = null;
|
||||
if (RawJsonReader.seekToKey(reader, this.key)) {
|
||||
id = reader.readString();
|
||||
} else if (parent != null) {
|
||||
id = this.getIdFor((Class<? extends T>)parent.getClass());
|
||||
}
|
||||
|
||||
reader.reset();
|
||||
extraInfo.ignoreUnusedKey(this.key);
|
||||
|
||||
JsonAsset var8;
|
||||
try {
|
||||
AssetBuilderCodec<K, T> codec = id == null ? null : this.idToCodec.get(id);
|
||||
if (codec == null) {
|
||||
AssetBuilderCodec<K, T> defaultCodec = this.getDefaultCodec();
|
||||
if (defaultCodec == null) {
|
||||
throw new ACodecMapCodec.UnknownIdException("No codec registered with for '" + this.key + "': " + id);
|
||||
}
|
||||
|
||||
codec = defaultCodec;
|
||||
}
|
||||
|
||||
Supplier<T> supplier = codec.getSupplier();
|
||||
if (supplier == null) {
|
||||
throw new CodecException(
|
||||
"This BuilderCodec is for an abstract or direct codec. To use this codec you must specify an existing object to decode into."
|
||||
);
|
||||
}
|
||||
|
||||
T t = supplier.get();
|
||||
this.dataSetter.accept(t, extraInfo.getData());
|
||||
if (parent != null) {
|
||||
codec.inherit(t, parent, extraInfo);
|
||||
}
|
||||
|
||||
codec.decodeAndInheritJson0(reader, t, parent, extraInfo);
|
||||
this.idSetter.accept(t, extraInfo.getKey());
|
||||
codec.afterDecodeAndValidate(t, extraInfo);
|
||||
var8 = t;
|
||||
} finally {
|
||||
extraInfo.popIgnoredUnusedKey();
|
||||
}
|
||||
|
||||
return (T)var8;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Schema toSchema(@Nonnull SchemaContext context) {
|
||||
Schema schema = super.toSchema(context);
|
||||
schema.getHytaleSchemaTypeField().setParentPropertyKey(this.parentCodec.getKey());
|
||||
return schema;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void mutateChildSchema(String key, @Nonnull SchemaContext context, BuilderCodec<? extends T> c, @Nonnull ObjectSchema objectSchema) {
|
||||
super.mutateChildSchema(key, context, c, objectSchema);
|
||||
AssetBuilderCodec<K, T> def = this.getDefaultCodec();
|
||||
if (!this.allowDefault || def != c) {
|
||||
Schema idField = new Schema();
|
||||
idField.setRequired(this.key);
|
||||
Schema parentField = new Schema();
|
||||
parentField.setRequired(this.parentCodec.getKey());
|
||||
AssetBuilderCodec<K, T> bc = (AssetBuilderCodec<K, T>)c;
|
||||
Schema parentSchema = objectSchema.getProperties().get(bc.getParentCodec().getKey());
|
||||
if (parentSchema != null) {
|
||||
Schema.InheritSettings settings = parentSchema.getHytaleParent();
|
||||
settings.setMapKey(this.key);
|
||||
settings.setMapKeyValue(key);
|
||||
objectSchema.setOneOf(idField, parentField);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
package com.hypixel.hytale.assetstore.codec;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetExtraInfo;
|
||||
import com.hypixel.hytale.assetstore.AssetMap;
|
||||
import com.hypixel.hytale.assetstore.AssetRegistry;
|
||||
import com.hypixel.hytale.assetstore.AssetStore;
|
||||
import com.hypixel.hytale.assetstore.RawAsset;
|
||||
import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.ExtraInfo;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.schema.SchemaContext;
|
||||
import com.hypixel.hytale.codec.schema.config.Schema;
|
||||
import com.hypixel.hytale.codec.util.RawJsonReader;
|
||||
import com.hypixel.hytale.codec.validation.ValidatableCodec;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import org.bson.BsonValue;
|
||||
|
||||
public class ContainedAssetCodec<K, T extends JsonAssetWithMap<K, M>, M extends AssetMap<K, T>> implements Codec<K>, ValidatableCodec<K> {
|
||||
private static final boolean DISABLE_DIRECT_LOADING = true;
|
||||
private final Class<T> assetClass;
|
||||
private final AssetCodec<K, T> codec;
|
||||
@Nonnull
|
||||
private final ContainedAssetCodec.Mode mode;
|
||||
private final Function<AssetExtraInfo<K>, K> keyGenerator;
|
||||
|
||||
public ContainedAssetCodec(Class<T> assetClass, AssetCodec<K, T> codec) {
|
||||
this(assetClass, codec, ContainedAssetCodec.Mode.GENERATE_ID);
|
||||
}
|
||||
|
||||
public ContainedAssetCodec(Class<T> assetClass, AssetCodec<K, T> codec, @Nonnull ContainedAssetCodec.Mode mode) {
|
||||
this(assetClass, codec, mode, assetExtraInfo -> AssetRegistry.<K, T, M>getAssetStore(assetClass).transformKey(assetExtraInfo.generateKey()));
|
||||
}
|
||||
|
||||
public ContainedAssetCodec(Class<T> assetClass, AssetCodec<K, T> codec, @Nonnull ContainedAssetCodec.Mode mode, Function<AssetExtraInfo<K>, K> keyGenerator) {
|
||||
if (mode == ContainedAssetCodec.Mode.NONE) {
|
||||
throw new UnsupportedOperationException("Contained asset mode can't be NONE!");
|
||||
} else {
|
||||
this.assetClass = assetClass;
|
||||
this.codec = codec;
|
||||
this.mode = mode;
|
||||
this.keyGenerator = keyGenerator;
|
||||
}
|
||||
}
|
||||
|
||||
public Class<T> getAssetClass() {
|
||||
return this.assetClass;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public K decode(@Nonnull BsonValue bsonValue, ExtraInfo extraInfo) {
|
||||
if (!(extraInfo instanceof AssetExtraInfo<K> assetExtraInfo)) {
|
||||
throw new UnsupportedOperationException("Unable to decode asset from codec used outside of an AssetStore");
|
||||
} else if (bsonValue.isString()) {
|
||||
return this.codec.getKeyCodec().getChildCodec().decode(bsonValue, extraInfo);
|
||||
} else {
|
||||
KeyedCodec<K> parentCodec = this.codec.getParentCodec();
|
||||
K parentId = parentCodec != null ? parentCodec.getOrNull(bsonValue.asDocument(), extraInfo) : null;
|
||||
AssetStore<K, T, M> assetStore = AssetRegistry.getAssetStore(this.assetClass);
|
||||
K id;
|
||||
switch (this.mode) {
|
||||
case GENERATE_ID: {
|
||||
id = this.keyGenerator.apply(assetExtraInfo);
|
||||
boolean inheritContainerTags = false;
|
||||
break;
|
||||
}
|
||||
case INHERIT_ID: {
|
||||
id = assetStore.transformKey(assetExtraInfo.getKey());
|
||||
boolean inheritContainerTags = true;
|
||||
break;
|
||||
}
|
||||
case INHERIT_ID_AND_PARENT: {
|
||||
id = assetStore.transformKey(assetExtraInfo.getKey());
|
||||
if (parentId == null) {
|
||||
Object thisAssetParentId = assetExtraInfo.getData().getParentKey();
|
||||
if (thisAssetParentId != null) {
|
||||
parentId = assetStore.transformKey(thisAssetParentId);
|
||||
}
|
||||
}
|
||||
|
||||
boolean inheritContainerTags = true;
|
||||
break;
|
||||
}
|
||||
case INJECT_PARENT: {
|
||||
id = this.keyGenerator.apply(assetExtraInfo);
|
||||
if (parentId == null && !assetExtraInfo.getKey().equals(id)) {
|
||||
parentId = assetExtraInfo.getKey();
|
||||
}
|
||||
|
||||
boolean inheritContainerTags = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new UnsupportedOperationException("Contained asset mode can't be NONE!");
|
||||
}
|
||||
|
||||
T parent = parentId != null ? assetStore.getAssetMap().getAsset(parentId) : null;
|
||||
if (parentId != null && parent != null) {
|
||||
}
|
||||
|
||||
char[] clone = bsonValue.asDocument().toJson().toCharArray();
|
||||
Path path = assetExtraInfo.getAssetPath();
|
||||
assetExtraInfo.getData().addContainedAsset(this.assetClass, new RawAsset<>(path, id, parentId, 0, clone, assetExtraInfo.getData(), this.mode));
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BsonValue encode(@Nonnull K key, ExtraInfo extraInfo) {
|
||||
if (key.toString().startsWith("*")) {
|
||||
T asset = (T)AssetRegistry.<K, T, M>getAssetStore(this.assetClass).getAssetMap().getAsset(key);
|
||||
if (asset != null) {
|
||||
return this.codec.encode(asset, extraInfo);
|
||||
}
|
||||
}
|
||||
|
||||
return this.codec.getKeyCodec().getChildCodec().encode(key, extraInfo);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public K decodeJson(@Nonnull RawJsonReader reader, ExtraInfo extraInfo) throws IOException {
|
||||
if (!(extraInfo instanceof AssetExtraInfo<K> assetExtraInfo)) {
|
||||
throw new UnsupportedOperationException("Unable to decode asset from codec used outside of an AssetStore");
|
||||
} else {
|
||||
int lineStart = reader.getLine() - 1;
|
||||
if (reader.peekFor('"')) {
|
||||
return this.codec.getKeyCodec().getChildCodec().decodeJson(reader, extraInfo);
|
||||
} else {
|
||||
reader.mark();
|
||||
K parentId = null;
|
||||
boolean needsSkip = false;
|
||||
KeyedCodec<K> parentCodec = this.codec.getParentCodec();
|
||||
if (parentCodec != null && RawJsonReader.seekToKey(reader, parentCodec.getKey())) {
|
||||
parentId = parentCodec.getChildCodec().decodeJson(reader, extraInfo);
|
||||
needsSkip = true;
|
||||
}
|
||||
|
||||
AssetStore<K, T, M> assetStore = AssetRegistry.getAssetStore(this.assetClass);
|
||||
K id;
|
||||
switch (this.mode) {
|
||||
case GENERATE_ID: {
|
||||
id = this.keyGenerator.apply(assetExtraInfo);
|
||||
boolean inheritContainerTags = false;
|
||||
break;
|
||||
}
|
||||
case INHERIT_ID: {
|
||||
id = assetStore.transformKey(assetExtraInfo.getKey());
|
||||
boolean inheritContainerTags = true;
|
||||
break;
|
||||
}
|
||||
case INHERIT_ID_AND_PARENT: {
|
||||
id = assetStore.transformKey(assetExtraInfo.getKey());
|
||||
if (parentId == null) {
|
||||
Object thisAssetParentId = assetExtraInfo.getData().getParentKey();
|
||||
if (thisAssetParentId != null) {
|
||||
parentId = assetStore.transformKey(thisAssetParentId);
|
||||
}
|
||||
}
|
||||
|
||||
boolean inheritContainerTags = true;
|
||||
break;
|
||||
}
|
||||
case INJECT_PARENT: {
|
||||
id = this.keyGenerator.apply(assetExtraInfo);
|
||||
if (parentId == null && !assetExtraInfo.getKey().equals(id)) {
|
||||
parentId = assetExtraInfo.getKey();
|
||||
}
|
||||
|
||||
boolean inheritContainerTags = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
throw new UnsupportedOperationException("Contained asset mode can't be NONE!");
|
||||
}
|
||||
|
||||
T parent = parentId != null ? assetStore.getAssetMap().getAsset(parentId) : null;
|
||||
if (parentId != null && parent != null) {
|
||||
}
|
||||
|
||||
if (needsSkip) {
|
||||
reader.skipObjectContinued();
|
||||
}
|
||||
|
||||
char[] clone = reader.cloneMark();
|
||||
reader.unmark();
|
||||
Path path = assetExtraInfo.getAssetPath();
|
||||
assetExtraInfo.getData()
|
||||
.addContainedAsset(this.assetClass, new RawAsset<>(path, id, parentId, lineStart, clone, assetExtraInfo.getData(), this.mode));
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Schema toSchema(@Nonnull SchemaContext context) {
|
||||
Schema keySchema = context.refDefinition(this.codec.getKeyCodec().getChildCodec());
|
||||
keySchema.setTitle("Reference to " + this.assetClass.getSimpleName());
|
||||
Schema nestedSchema = context.refDefinition(this.codec);
|
||||
Schema s = Schema.anyOf(keySchema, nestedSchema);
|
||||
s.setHytaleAssetRef(this.assetClass.getSimpleName());
|
||||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validate(K k, @Nonnull ExtraInfo extraInfo) {
|
||||
AssetRegistry.<K, T, M>getAssetStore(this.assetClass).validate(k, extraInfo.getValidationResults(), extraInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateDefaults(ExtraInfo extraInfo, @Nonnull Set<Codec<?>> tested) {
|
||||
if (tested.add(this)) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
public static enum Mode {
|
||||
NONE,
|
||||
GENERATE_ID,
|
||||
INHERIT_ID,
|
||||
INHERIT_ID_AND_PARENT,
|
||||
INJECT_PARENT;
|
||||
|
||||
private Mode() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.hypixel.hytale.assetstore.event;
|
||||
|
||||
import com.hypixel.hytale.event.IEvent;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public abstract class AssetMonitorEvent<T> implements IEvent<T> {
|
||||
@Nonnull
|
||||
private final List<Path> createdOrModifiedFilesToLoad;
|
||||
@Nonnull
|
||||
private final List<Path> removedFilesToUnload;
|
||||
@Nonnull
|
||||
private final List<Path> createdOrModifiedDirectories;
|
||||
@Nonnull
|
||||
private final List<Path> removedFilesAndDirectories;
|
||||
@Nonnull
|
||||
private final String assetPack;
|
||||
|
||||
public AssetMonitorEvent(
|
||||
@Nonnull String assetPack,
|
||||
@Nonnull List<Path> createdOrModified,
|
||||
@Nonnull List<Path> removed,
|
||||
@Nonnull List<Path> createdDirectories,
|
||||
@Nonnull List<Path> removedDirectories
|
||||
) {
|
||||
this.assetPack = assetPack;
|
||||
this.createdOrModifiedFilesToLoad = createdOrModified;
|
||||
this.removedFilesToUnload = removed;
|
||||
this.createdOrModifiedDirectories = createdDirectories;
|
||||
this.removedFilesAndDirectories = removedDirectories;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public String getAssetPack() {
|
||||
return this.assetPack;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public List<Path> getCreatedOrModifiedFilesToLoad() {
|
||||
return this.createdOrModifiedFilesToLoad;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public List<Path> getRemovedFilesToUnload() {
|
||||
return this.removedFilesToUnload;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public List<Path> getRemovedFilesAndDirectories() {
|
||||
return this.removedFilesAndDirectories;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public List<Path> getCreatedOrModifiedDirectories() {
|
||||
return this.createdOrModifiedDirectories;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.hypixel.hytale.assetstore.event;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetStore;
|
||||
import com.hypixel.hytale.event.IEvent;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public abstract class AssetStoreEvent<KeyType> implements IEvent<KeyType> {
|
||||
@Nonnull
|
||||
private final AssetStore<?, ?, ?> assetStore;
|
||||
|
||||
public AssetStoreEvent(@Nonnull AssetStore<?, ?, ?> assetStore) {
|
||||
this.assetStore = assetStore;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public AssetStore<?, ?, ?> getAssetStore() {
|
||||
return this.assetStore;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AssetStoreEvent{assetStore=" + this.assetStore + "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.hypixel.hytale.assetstore.event;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetStore;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class AssetStoreMonitorEvent extends AssetMonitorEvent<Void> {
|
||||
@Nonnull
|
||||
private final AssetStore<?, ?, ?> assetStore;
|
||||
|
||||
public AssetStoreMonitorEvent(
|
||||
@Nonnull String assetPack,
|
||||
@Nonnull AssetStore<?, ?, ?> assetStore,
|
||||
@Nonnull List<Path> createdOrModified,
|
||||
@Nonnull List<Path> removed,
|
||||
@Nonnull List<Path> createdOrModifiedDirectories,
|
||||
@Nonnull List<Path> removedDirectories
|
||||
) {
|
||||
super(assetPack, createdOrModified, removed, createdOrModifiedDirectories, removedDirectories);
|
||||
this.assetStore = assetStore;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public AssetStore<?, ?, ?> getAssetStore() {
|
||||
return this.assetStore;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AssetMonitorEvent{assetStore=" + this.assetStore + "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package com.hypixel.hytale.assetstore.event;
|
||||
|
||||
import com.hypixel.hytale.assetstore.JsonAsset;
|
||||
import com.hypixel.hytale.event.IEvent;
|
||||
|
||||
public abstract class AssetsEvent<K, T extends JsonAsset<K>> implements IEvent<Class<T>> {
|
||||
public AssetsEvent() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package com.hypixel.hytale.assetstore.event;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetExtraInfo;
|
||||
import com.hypixel.hytale.assetstore.AssetMap;
|
||||
import com.hypixel.hytale.assetstore.AssetRegistry;
|
||||
import com.hypixel.hytale.assetstore.AssetStore;
|
||||
import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;
|
||||
import com.hypixel.hytale.common.util.FormatUtil;
|
||||
import com.hypixel.hytale.event.IProcessedEvent;
|
||||
import com.hypixel.hytale.logger.HytaleLogger;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Level;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class GenerateAssetsEvent<K, T extends JsonAssetWithMap<K, M>, M extends AssetMap<K, T>> extends AssetsEvent<K, T> implements IProcessedEvent {
|
||||
private final Class<T> tClass;
|
||||
private final M assetMap;
|
||||
@Nonnull
|
||||
private final Map<K, T> loadedAssets;
|
||||
private final Map<K, Set<K>> assetChildren;
|
||||
@Nonnull
|
||||
private final Map<K, T> unmodifiableLoadedAssets;
|
||||
private final Map<K, T> addedAssets = new ConcurrentHashMap<>();
|
||||
private final Map<K, Set<K>> addedAssetChildren = new ConcurrentHashMap<>();
|
||||
private final Map<Class<? extends JsonAssetWithMap<?, ?>>, Map<?, Set<K>>> addedChildAssetsMap = new ConcurrentHashMap<>();
|
||||
private long before;
|
||||
|
||||
public GenerateAssetsEvent(Class<T> tClass, M assetMap, @Nonnull Map<K, T> loadedAssets, Map<K, Set<K>> assetChildren) {
|
||||
this.tClass = tClass;
|
||||
this.assetMap = assetMap;
|
||||
this.loadedAssets = loadedAssets;
|
||||
this.assetChildren = assetChildren;
|
||||
this.unmodifiableLoadedAssets = Collections.unmodifiableMap(loadedAssets);
|
||||
this.before = System.nanoTime();
|
||||
}
|
||||
|
||||
public Class<T> getAssetClass() {
|
||||
return this.tClass;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Map<K, T> getLoadedAssets() {
|
||||
return this.unmodifiableLoadedAssets;
|
||||
}
|
||||
|
||||
public M getAssetMap() {
|
||||
return this.assetMap;
|
||||
}
|
||||
|
||||
public void addChildAsset(K childKey, T asset, @Nonnull K parent) {
|
||||
if (!this.loadedAssets.containsKey(parent) && this.assetMap.getAsset(parent) == null) {
|
||||
throw new IllegalArgumentException("Parent '" + parent + "' doesn't exist!");
|
||||
} else if (parent.equals(childKey)) {
|
||||
throw new IllegalArgumentException("Unable to to add asset '" + parent + "' because it is its own parent!");
|
||||
} else {
|
||||
AssetStore<K, T, M> assetStore = AssetRegistry.getAssetStore(this.tClass);
|
||||
AssetExtraInfo<K> extraInfo = new AssetExtraInfo<>(assetStore.getCodec().getData(asset));
|
||||
assetStore.getCodec().validate(asset, extraInfo);
|
||||
extraInfo.getValidationResults().logOrThrowValidatorExceptions(assetStore.getLogger());
|
||||
this.addedAssets.put(childKey, asset);
|
||||
this.addedAssetChildren.computeIfAbsent(parent, k -> new HashSet<>()).add(childKey);
|
||||
}
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final void addChildAsset(K childKey, T asset, @Nonnull K... parents) {
|
||||
for (int i = 0; i < parents.length; i++) {
|
||||
K parent = parents[i];
|
||||
if (!this.loadedAssets.containsKey(parent) && this.assetMap.getAsset(parent) == null) {
|
||||
throw new IllegalArgumentException("Parent at " + i + " '" + parent + "' doesn't exist!");
|
||||
}
|
||||
|
||||
if (parent.equals(childKey)) {
|
||||
throw new IllegalArgumentException("Unable to to add asset '" + parent + "' because it is its own parent!");
|
||||
}
|
||||
}
|
||||
|
||||
AssetStore<K, T, M> assetStore = AssetRegistry.getAssetStore(this.tClass);
|
||||
AssetExtraInfo<K> extraInfo = new AssetExtraInfo<>(assetStore.getCodec().getData(asset));
|
||||
assetStore.getCodec().validate(asset, extraInfo);
|
||||
extraInfo.getValidationResults().logOrThrowValidatorExceptions(assetStore.getLogger());
|
||||
this.addedAssets.put(childKey, asset);
|
||||
|
||||
for (K parentx : parents) {
|
||||
this.addedAssetChildren.computeIfAbsent(parentx, k -> new HashSet<>()).add(childKey);
|
||||
}
|
||||
}
|
||||
|
||||
public <P extends JsonAssetWithMap<PK, ?>, PK> void addChildAssetWithReference(K childKey, T asset, Class<P> parentAssetClass, @Nonnull PK parentKey) {
|
||||
if (AssetRegistry.<PK, T, AssetMap<PK, T>>getAssetStore(parentAssetClass).getAssetMap().getAsset(parentKey) == null) {
|
||||
throw new IllegalArgumentException("Parent '" + parentKey + "' from " + parentAssetClass + " doesn't exist!");
|
||||
} else if (parentKey.equals(childKey)) {
|
||||
throw new IllegalArgumentException("Unable to to add asset '" + parentKey + "' because it is its own parent!");
|
||||
} else {
|
||||
AssetStore<K, T, M> assetStore = AssetRegistry.getAssetStore(this.tClass);
|
||||
AssetExtraInfo<K> extraInfo = new AssetExtraInfo<>(assetStore.getCodec().getData(asset));
|
||||
assetStore.getCodec().validate(asset, extraInfo);
|
||||
extraInfo.getValidationResults().logOrThrowValidatorExceptions(assetStore.getLogger());
|
||||
this.addedAssets.put(childKey, asset);
|
||||
((Map<PK, Set<K>>)this.addedChildAssetsMap
|
||||
.computeIfAbsent(parentAssetClass, k -> new ConcurrentHashMap<>()))
|
||||
.computeIfAbsent(parentKey, k -> new HashSet())
|
||||
.add(childKey);
|
||||
}
|
||||
}
|
||||
|
||||
public void addChildAssetWithReferences(K childKey, T asset, @Nonnull GenerateAssetsEvent.ParentReference<?, ?>... parents) {
|
||||
for (int i = 0; i < parents.length; i++) {
|
||||
GenerateAssetsEvent.ParentReference<?, ?> parent = parents[i];
|
||||
if (AssetRegistry.getAssetStore(parent.getParentAssetClass()).getAssetMap().getAsset((K)parent.getParentKey()) == null) {
|
||||
throw new IllegalArgumentException("Parent at " + i + " '" + parent + "' doesn't exist!");
|
||||
}
|
||||
|
||||
if (parent.parentKey.equals(childKey)) {
|
||||
throw new IllegalArgumentException("Unable to to add asset '" + parent.parentKey + "' because it is its own parent!");
|
||||
}
|
||||
}
|
||||
|
||||
AssetStore<K, T, M> assetStore = AssetRegistry.getAssetStore(this.tClass);
|
||||
AssetExtraInfo<K> extraInfo = new AssetExtraInfo<>(assetStore.getCodec().getData(asset));
|
||||
assetStore.getCodec().validate(asset, extraInfo);
|
||||
extraInfo.getValidationResults().logOrThrowValidatorExceptions(assetStore.getLogger());
|
||||
this.addedAssets.put(childKey, asset);
|
||||
|
||||
for (GenerateAssetsEvent.ParentReference<?, ?> parentx : parents) {
|
||||
((Map<Object, Set<K>>)this.addedChildAssetsMap
|
||||
.computeIfAbsent((Class<? extends JsonAssetWithMap<?, ?>>)parentx.parentAssetClass, k -> new ConcurrentHashMap<>()))
|
||||
.computeIfAbsent(parentx.parentKey, k -> new HashSet())
|
||||
.add(childKey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processEvent(@Nonnull String hookName) {
|
||||
HytaleLogger.getLogger()
|
||||
.at(Level.INFO)
|
||||
.log(
|
||||
"Generated %d of %s from %s in %s",
|
||||
this.addedAssets.size(),
|
||||
this.tClass.getSimpleName(),
|
||||
hookName,
|
||||
FormatUtil.nanosToString(System.nanoTime() - this.before)
|
||||
);
|
||||
this.loadedAssets.putAll(this.addedAssets);
|
||||
this.addedAssets.clear();
|
||||
|
||||
for (Entry<K, Set<K>> entry : this.addedAssetChildren.entrySet()) {
|
||||
K parent = entry.getKey();
|
||||
this.assetChildren.computeIfAbsent(parent, kx -> ConcurrentHashMap.newKeySet()).addAll(entry.getValue());
|
||||
}
|
||||
|
||||
this.addedAssetChildren.clear();
|
||||
|
||||
for (Entry<Class<? extends JsonAssetWithMap<?, ?>>, Map<?, Set<K>>> entry : this.addedChildAssetsMap.entrySet()) {
|
||||
Class k = entry.getKey();
|
||||
AssetStore assetStore = AssetRegistry.getAssetStore(k);
|
||||
|
||||
for (Entry<?, Set<K>> childEntry : entry.getValue().entrySet()) {
|
||||
assetStore.addChildAssetReferences(childEntry.getKey(), this.tClass, childEntry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
this.addedChildAssetsMap.clear();
|
||||
this.before = System.nanoTime();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GenerateAssetsEvent{tClass=" + this.tClass + ", loadedAssets.size()=" + this.loadedAssets.size() + ", " + super.toString() + "}";
|
||||
}
|
||||
|
||||
public static class ParentReference<P extends JsonAssetWithMap<PK, ?>, PK> {
|
||||
private final Class<P> parentAssetClass;
|
||||
private final PK parentKey;
|
||||
|
||||
public ParentReference(Class<P> parentAssetClass, PK parentKey) {
|
||||
this.parentAssetClass = parentAssetClass;
|
||||
this.parentKey = parentKey;
|
||||
}
|
||||
|
||||
public Class<P> getParentAssetClass() {
|
||||
return this.parentAssetClass;
|
||||
}
|
||||
|
||||
public PK getParentKey() {
|
||||
return this.parentKey;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ParentReference{parentAssetClass=" + this.parentAssetClass + ", parentKey=" + this.parentKey + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.hypixel.hytale.assetstore.event;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetMap;
|
||||
import com.hypixel.hytale.assetstore.AssetUpdateQuery;
|
||||
import com.hypixel.hytale.assetstore.JsonAsset;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class LoadedAssetsEvent<K, T extends JsonAsset<K>, M extends AssetMap<K, T>> extends AssetsEvent<K, T> {
|
||||
@Nonnull
|
||||
private final Class<T> tClass;
|
||||
@Nonnull
|
||||
private final M assetMap;
|
||||
@Nonnull
|
||||
private final Map<K, T> loadedAssets;
|
||||
private final boolean initial;
|
||||
@Nonnull
|
||||
private final AssetUpdateQuery query;
|
||||
|
||||
public LoadedAssetsEvent(@Nonnull Class<T> tClass, @Nonnull M assetMap, @Nonnull Map<K, T> loadedAssets, boolean initial, @Nonnull AssetUpdateQuery query) {
|
||||
this.tClass = tClass;
|
||||
this.assetMap = assetMap;
|
||||
this.loadedAssets = Collections.unmodifiableMap(loadedAssets);
|
||||
this.initial = initial;
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
public Class<T> getAssetClass() {
|
||||
return this.tClass;
|
||||
}
|
||||
|
||||
public M getAssetMap() {
|
||||
return this.assetMap;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Map<K, T> getLoadedAssets() {
|
||||
return this.loadedAssets;
|
||||
}
|
||||
|
||||
public boolean isInitial() {
|
||||
return this.initial;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public AssetUpdateQuery getQuery() {
|
||||
return this.query;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LoadedAssetsEvent{loadedAssets=" + this.loadedAssets + ", initial=" + this.initial + ", query=" + this.query + "} " + super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.hypixel.hytale.assetstore.event;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetStore;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class RegisterAssetStoreEvent extends AssetStoreEvent<Void> {
|
||||
public RegisterAssetStoreEvent(@Nonnull AssetStore<?, ?, ?> assetStore) {
|
||||
super(assetStore);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.hypixel.hytale.assetstore.event;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetStore;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class RemoveAssetStoreEvent extends AssetStoreEvent<Void> {
|
||||
public RemoveAssetStoreEvent(@Nonnull AssetStore<?, ?, ?> assetStore) {
|
||||
super(assetStore);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.hypixel.hytale.assetstore.event;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetMap;
|
||||
import com.hypixel.hytale.assetstore.JsonAsset;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class RemovedAssetsEvent<K, T extends JsonAsset<K>, M extends AssetMap<K, T>> extends AssetsEvent<K, T> {
|
||||
private final Class<T> tClass;
|
||||
private final M assetMap;
|
||||
@Nonnull
|
||||
private final Set<K> removedAssets;
|
||||
private final boolean replaced;
|
||||
|
||||
public RemovedAssetsEvent(Class<T> tClass, M assetMap, @Nonnull Set<K> removedAssets, boolean replaced) {
|
||||
this.tClass = tClass;
|
||||
this.assetMap = assetMap;
|
||||
this.removedAssets = Collections.unmodifiableSet(removedAssets);
|
||||
this.replaced = replaced;
|
||||
}
|
||||
|
||||
public Class<T> getAssetClass() {
|
||||
return this.tClass;
|
||||
}
|
||||
|
||||
public M getAssetMap() {
|
||||
return this.assetMap;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Set<K> getRemovedAssets() {
|
||||
return this.removedAssets;
|
||||
}
|
||||
|
||||
public boolean isReplaced() {
|
||||
return this.replaced;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RemovedAssetsEvent{removedAssets=" + this.removedAssets + ", replaced=" + this.replaced + "} " + super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.hypixel.hytale.assetstore.iterator;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetMap;
|
||||
import com.hypixel.hytale.assetstore.AssetRegistry;
|
||||
import com.hypixel.hytale.assetstore.AssetStore;
|
||||
import com.hypixel.hytale.assetstore.JsonAsset;
|
||||
import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;
|
||||
import java.io.Closeable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class AssetStoreIterator implements Iterator<AssetStore<?, ?, ?>>, Closeable {
|
||||
@Nonnull
|
||||
private final List<AssetStore<?, ?, ?>> list;
|
||||
|
||||
public AssetStoreIterator(@Nonnull Collection<AssetStore<?, ?, ?>> values) {
|
||||
this.list = new ArrayList<>(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return !this.list.isEmpty();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AssetStore<?, ?, ?> next() {
|
||||
Iterator<AssetStore<?, ?, ?>> iterator = this.list.iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
AssetStore<?, ? extends JsonAssetWithMap<?, ? extends AssetMap<?, ?>>, ? extends AssetMap<?, ? extends JsonAssetWithMap<?, ?>>> assetStore = (AssetStore<?, ? extends JsonAssetWithMap<?, ? extends AssetMap<?, ?>>, ? extends AssetMap<?, ? extends JsonAssetWithMap<?, ?>>>)iterator.next();
|
||||
if (!this.isWaitingForDependencies(assetStore)) {
|
||||
iterator.remove();
|
||||
return assetStore;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return this.list.size();
|
||||
}
|
||||
|
||||
public boolean isWaitingForDependencies(@Nonnull AssetStore<?, ?, ?> assetStore) {
|
||||
for (Class<? extends JsonAsset<?>> aClass : assetStore.getLoadsAfter()) {
|
||||
AssetStore otherStore = AssetRegistry.getAssetStore(aClass);
|
||||
if (otherStore == null) {
|
||||
throw new IllegalArgumentException("Unable to find asset store: " + aClass);
|
||||
}
|
||||
|
||||
if (this.list.contains(otherStore)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isBeingWaitedFor(@Nonnull AssetStore<?, ?, ?> assetStore) {
|
||||
Class<? extends JsonAssetWithMap<?, ? extends AssetMap<?, ?>>> assetClass = (Class<? extends JsonAssetWithMap<?, ? extends AssetMap<?, ?>>>)assetStore.getAssetClass();
|
||||
|
||||
for (AssetStore<?, ?, ?> store : this.list) {
|
||||
if (store.getLoadsAfter().contains(assetClass)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.hypixel.hytale.assetstore.iterator;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetRegistry;
|
||||
import com.hypixel.hytale.assetstore.AssetStore;
|
||||
import com.hypixel.hytale.assetstore.JsonAsset;
|
||||
import java.util.Collection;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class CircularDependencyException extends RuntimeException {
|
||||
public CircularDependencyException(@Nonnull Collection<AssetStore<?, ?, ?>> values, @Nonnull AssetStoreIterator iterator) {
|
||||
super(makeMessage(values, iterator));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected static String makeMessage(@Nonnull Collection<AssetStore<?, ?, ?>> values, @Nonnull AssetStoreIterator iterator) {
|
||||
StringBuilder sb = new StringBuilder(
|
||||
"Failed to process any stores there must be a circular dependency! " + values + ", " + iterator.size() + "\nWaiting for Asset Stores:\n"
|
||||
);
|
||||
|
||||
for (AssetStore<?, ?, ?> store : values) {
|
||||
if (iterator.isWaitingForDependencies(store)) {
|
||||
sb.append(store.getAssetClass()).append("\n");
|
||||
|
||||
for (Class<? extends JsonAsset<?>> aClass : store.getLoadsAfter()) {
|
||||
AssetStore otherStore = AssetRegistry.getAssetStore(aClass);
|
||||
if (otherStore == null) {
|
||||
throw new IllegalArgumentException("Unable to find asset store: " + aClass);
|
||||
}
|
||||
|
||||
if (iterator.isWaitingForDependencies(otherStore)) {
|
||||
sb.append("\t- ").append(otherStore.getAssetClass()).append("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.hypixel.hytale.assetstore.map;
|
||||
|
||||
import com.hypixel.fastutil.ints.Int2ObjectConcurrentHashMap;
|
||||
import com.hypixel.hytale.assetstore.AssetExtraInfo;
|
||||
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 java.util.Map;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public abstract class AssetMapWithIndexes<K, T extends JsonAsset<K>> extends DefaultAssetMap<K, T> {
|
||||
public static final int NOT_FOUND = Integer.MIN_VALUE;
|
||||
protected final Int2ObjectConcurrentHashMap<IntSet> indexedTagStorage = new Int2ObjectConcurrentHashMap<>();
|
||||
protected final Int2ObjectConcurrentHashMap<IntSet> unmodifiableIndexedTagStorage = new Int2ObjectConcurrentHashMap<>();
|
||||
|
||||
public AssetMapWithIndexes() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
super.clear();
|
||||
this.indexedTagStorage.clear();
|
||||
this.unmodifiableIndexedTagStorage.clear();
|
||||
}
|
||||
|
||||
public IntSet getIndexesForTag(int index) {
|
||||
return this.unmodifiableIndexedTagStorage.getOrDefault(index, IntSets.EMPTY_SET);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void putAssetTags(AssetCodec<K, T> codec, Map<K, T> loadedAssets) {
|
||||
}
|
||||
|
||||
protected void putAssetTag(@Nonnull AssetCodec<K, T> codec, K key, int index, T value) {
|
||||
AssetExtraInfo.Data data = codec.getData(value);
|
||||
if (data != null) {
|
||||
IntIterator iterator = data.getExpandedTagIndexes().iterator();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
int tag = iterator.nextInt();
|
||||
this.putAssetTag(key, index, tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void putAssetTag(K key, int index, int tag) {
|
||||
this.putAssetTag(key, tag);
|
||||
this.indexedTagStorage.computeIfAbsent(tag, k -> {
|
||||
IntSet set = Int2ObjectConcurrentHashMap.newKeySet(3);
|
||||
this.unmodifiableIndexedTagStorage.put(k, IntSets.unmodifiable(set));
|
||||
return set;
|
||||
}).add(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requireReplaceOnRemove() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package com.hypixel.hytale.assetstore.map;
|
||||
|
||||
import com.hypixel.hytale.assetstore.codec.AssetCodec;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSets;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntFunction;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockTypeAssetMap<K, T extends JsonAssetWithMap<K, BlockTypeAssetMap<K, T>>> extends AssetMapWithIndexes<K, T> {
|
||||
private final AtomicInteger nextIndex = new AtomicInteger();
|
||||
private final StampedLock keyToIndexLock = new StampedLock();
|
||||
private final Object2IntMap<K> keyToIndex = new Object2IntOpenCustomHashMap<>(CaseInsensitiveHashStrategy.getInstance());
|
||||
@Nonnull
|
||||
private final IntFunction<T[]> arrayProvider;
|
||||
private final ReentrantLock arrayLock = new ReentrantLock();
|
||||
private T[] array;
|
||||
private final Map<K, ObjectSet<K>> subKeyMap = new Object2ObjectOpenCustomHashMap<>(CaseInsensitiveHashStrategy.getInstance());
|
||||
@Deprecated
|
||||
private final Function<T, String> groupGetter;
|
||||
@Deprecated
|
||||
private final Object2IntMap<String> groupMap = new Object2IntOpenHashMap<>();
|
||||
|
||||
public BlockTypeAssetMap(@Nonnull IntFunction<T[]> arrayProvider, Function<T, String> groupGetter) {
|
||||
this.arrayProvider = arrayProvider;
|
||||
this.groupGetter = groupGetter;
|
||||
this.array = (T[])((JsonAssetWithMap[])arrayProvider.apply(0));
|
||||
this.keyToIndex.defaultReturnValue(Integer.MIN_VALUE);
|
||||
}
|
||||
|
||||
public int getIndex(K key) {
|
||||
long stamp = this.keyToIndexLock.tryOptimisticRead();
|
||||
int value = this.keyToIndex.getInt(key);
|
||||
if (this.keyToIndexLock.validate(stamp)) {
|
||||
return value;
|
||||
} else {
|
||||
stamp = this.keyToIndexLock.readLock();
|
||||
|
||||
int var5;
|
||||
try {
|
||||
var5 = this.keyToIndex.getInt(key);
|
||||
} finally {
|
||||
this.keyToIndexLock.unlockRead(stamp);
|
||||
}
|
||||
|
||||
return var5;
|
||||
}
|
||||
}
|
||||
|
||||
public int getIndexOrDefault(K key, int def) {
|
||||
long stamp = this.keyToIndexLock.tryOptimisticRead();
|
||||
int value = this.keyToIndex.getOrDefault(key, def);
|
||||
if (this.keyToIndexLock.validate(stamp)) {
|
||||
return value;
|
||||
} else {
|
||||
stamp = this.keyToIndexLock.readLock();
|
||||
|
||||
int var6;
|
||||
try {
|
||||
var6 = this.keyToIndex.getOrDefault(key, def);
|
||||
} finally {
|
||||
this.keyToIndexLock.unlockRead(stamp);
|
||||
}
|
||||
|
||||
return var6;
|
||||
}
|
||||
}
|
||||
|
||||
public int getNextIndex() {
|
||||
this.arrayLock.lock();
|
||||
|
||||
int var1;
|
||||
try {
|
||||
var1 = this.array.length;
|
||||
} finally {
|
||||
this.arrayLock.unlock();
|
||||
}
|
||||
|
||||
return var1;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T getAsset(int index) {
|
||||
return index >= 0 && index < this.array.length ? this.array[index] : null;
|
||||
}
|
||||
|
||||
public T getAssetOrDefault(int index, T def) {
|
||||
return index >= 0 && index < this.array.length ? this.array[index] : def;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public ObjectSet<K> getSubKeys(K key) {
|
||||
ObjectSet<K> subKeySet = this.subKeyMap.get(key);
|
||||
return subKeySet != null ? ObjectSets.unmodifiable(subKeySet) : ObjectSets.singleton(key);
|
||||
}
|
||||
|
||||
public int getGroupId(String group) {
|
||||
return this.groupMap.getInt(group);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public String[] getGroups() {
|
||||
return this.groupMap.keySet().toArray(String[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
super.clear();
|
||||
long stamp = this.keyToIndexLock.writeLock();
|
||||
this.arrayLock.lock();
|
||||
|
||||
try {
|
||||
this.keyToIndex.clear();
|
||||
this.array = (T[])((JsonAssetWithMap[])this.arrayProvider.apply(0));
|
||||
} finally {
|
||||
this.arrayLock.unlock();
|
||||
this.keyToIndexLock.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
|
||||
) {
|
||||
super.putAll(packKey, codec, loadedAssets, loadedKeyToPathMap, loadedAssetChildren);
|
||||
this.putAll0(codec, loadedAssets);
|
||||
}
|
||||
|
||||
private void putAll0(@Nonnull AssetCodec<K, T> codec, @Nonnull Map<K, T> loadedAssets) {
|
||||
long stamp = this.keyToIndexLock.writeLock();
|
||||
this.arrayLock.lock();
|
||||
|
||||
try {
|
||||
int highestIndex = 0;
|
||||
|
||||
for (K key : loadedAssets.keySet()) {
|
||||
int index = this.keyToIndex.getInt(key);
|
||||
if (index == Integer.MIN_VALUE) {
|
||||
this.keyToIndex.put(key, index = this.nextIndex.getAndIncrement());
|
||||
}
|
||||
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("Index can't be less than zero!");
|
||||
}
|
||||
|
||||
if (index > highestIndex) {
|
||||
highestIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
int length = highestIndex + 1;
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Highest index can't be less than zero!");
|
||||
}
|
||||
|
||||
if (length > this.array.length) {
|
||||
T[] newArray = (T[])((JsonAssetWithMap[])this.arrayProvider.apply(length));
|
||||
System.arraycopy(this.array, 0, newArray, 0, this.array.length);
|
||||
this.array = newArray;
|
||||
}
|
||||
|
||||
for (Entry<K, T> entry : loadedAssets.entrySet()) {
|
||||
K key = entry.getKey();
|
||||
int indexx = this.keyToIndex.getInt(key);
|
||||
if (indexx < 0) {
|
||||
throw new IllegalArgumentException("Index can't be less than zero!");
|
||||
}
|
||||
|
||||
T value = entry.getValue();
|
||||
this.array[indexx] = value;
|
||||
ObjectSet<K> subKeySet = this.subKeyMap.get(key);
|
||||
if (subKeySet != null) {
|
||||
subKeySet.add(key);
|
||||
}
|
||||
|
||||
String group = this.groupGetter.apply(value);
|
||||
if (!this.groupMap.containsKey(group)) {
|
||||
int groupIndex = this.groupMap.size();
|
||||
this.groupMap.put(group, groupIndex);
|
||||
}
|
||||
|
||||
this.putAssetTag(codec, key, indexx, value);
|
||||
}
|
||||
} finally {
|
||||
this.arrayLock.unlock();
|
||||
this.keyToIndexLock.unlockWrite(stamp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<K> remove(@Nonnull Set<K> keys) {
|
||||
Set<K> remove = super.remove(keys);
|
||||
this.remove0(keys);
|
||||
return remove;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<K> remove(@Nonnull String packKey, @Nonnull Set<K> keys, @Nonnull List<Entry<String, Object>> pathsToReload) {
|
||||
Set<K> remove = super.remove(packKey, keys, pathsToReload);
|
||||
this.remove0(keys);
|
||||
return remove;
|
||||
}
|
||||
|
||||
private void remove0(@Nonnull Set<K> keys) {
|
||||
long stamp = this.keyToIndexLock.writeLock();
|
||||
this.arrayLock.lock();
|
||||
|
||||
try {
|
||||
for (K key : keys) {
|
||||
int blockId = this.keyToIndex.getInt(key);
|
||||
if (blockId != Integer.MIN_VALUE) {
|
||||
this.array[blockId] = null;
|
||||
this.indexedTagStorage.forEachWithInt((_k, value, id) -> value.remove(id), blockId);
|
||||
}
|
||||
|
||||
ObjectSet<K> subKeySet = this.subKeyMap.get(key);
|
||||
if (subKeySet != null) {
|
||||
subKeySet.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
int i = this.array.length - 1;
|
||||
|
||||
while (i > 0 && this.array[i] == null) {
|
||||
i--;
|
||||
}
|
||||
|
||||
int length = i + 1;
|
||||
if (length != this.array.length) {
|
||||
T[] newArray = (T[])((JsonAssetWithMap[])this.arrayProvider.apply(length));
|
||||
System.arraycopy(this.array, 0, newArray, 0, newArray.length);
|
||||
this.array = newArray;
|
||||
}
|
||||
} finally {
|
||||
this.arrayLock.unlock();
|
||||
this.keyToIndexLock.unlockWrite(stamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.hypixel.hytale.assetstore.map;
|
||||
|
||||
import it.unimi.dsi.fastutil.Hash.Strategy;
|
||||
|
||||
public class CaseInsensitiveHashStrategy<K> implements Strategy<K> {
|
||||
private static final CaseInsensitiveHashStrategy INSTANCE = new CaseInsensitiveHashStrategy();
|
||||
|
||||
public CaseInsensitiveHashStrategy() {
|
||||
}
|
||||
|
||||
public static <K> CaseInsensitiveHashStrategy<K> getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(K key) {
|
||||
if (key == null) {
|
||||
return 0;
|
||||
} else if (!(key instanceof String s)) {
|
||||
return key.hashCode();
|
||||
} else {
|
||||
int hash = 0;
|
||||
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
hash = 31 * hash + Character.toLowerCase(s.charAt(i));
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(K a, K b) {
|
||||
if (a == b) {
|
||||
return true;
|
||||
} else if (a != null && b != null) {
|
||||
return a instanceof String sa && b instanceof String sb ? sa.equalsIgnoreCase(sb) : a.equals(b);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
503
generated/com/hypixel/hytale/assetstore/map/DefaultAssetMap.java
Normal file
503
generated/com/hypixel/hytale/assetstore/map/DefaultAssetMap.java
Normal file
@@ -0,0 +1,503 @@
|
||||
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 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
131
generated/com/hypixel/hytale/assetstore/map/IndexedAssetMap.java
Normal file
131
generated/com/hypixel/hytale/assetstore/map/IndexedAssetMap.java
Normal file
@@ -0,0 +1,131 @@
|
||||
package com.hypixel.hytale.assetstore.map;
|
||||
|
||||
import com.hypixel.hytale.assetstore.codec.AssetCodec;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class IndexedAssetMap<K, T extends JsonAssetWithMap<K, IndexedAssetMap<K, T>>> extends AssetMapWithIndexes<K, T> {
|
||||
private final AtomicInteger nextIndex = new AtomicInteger();
|
||||
private final StampedLock keyToIndexLock = new StampedLock();
|
||||
private final Object2IntMap<K> keyToIndex = new Object2IntOpenCustomHashMap<>(CaseInsensitiveHashStrategy.getInstance());
|
||||
|
||||
public IndexedAssetMap() {
|
||||
this.keyToIndex.defaultReturnValue(Integer.MIN_VALUE);
|
||||
}
|
||||
|
||||
public int getIndex(K key) {
|
||||
long stamp = this.keyToIndexLock.tryOptimisticRead();
|
||||
int value = this.keyToIndex.getInt(key);
|
||||
if (this.keyToIndexLock.validate(stamp)) {
|
||||
return value;
|
||||
} else {
|
||||
stamp = this.keyToIndexLock.readLock();
|
||||
|
||||
int var5;
|
||||
try {
|
||||
var5 = this.keyToIndex.getInt(key);
|
||||
} finally {
|
||||
this.keyToIndexLock.unlockRead(stamp);
|
||||
}
|
||||
|
||||
return var5;
|
||||
}
|
||||
}
|
||||
|
||||
public int getIndexOrDefault(K key, int def) {
|
||||
long stamp = this.keyToIndexLock.tryOptimisticRead();
|
||||
int value = this.keyToIndex.getOrDefault(key, def);
|
||||
if (this.keyToIndexLock.validate(stamp)) {
|
||||
return value;
|
||||
} else {
|
||||
stamp = this.keyToIndexLock.readLock();
|
||||
|
||||
int var6;
|
||||
try {
|
||||
var6 = this.keyToIndex.getOrDefault(key, def);
|
||||
} finally {
|
||||
this.keyToIndexLock.unlockRead(stamp);
|
||||
}
|
||||
|
||||
return var6;
|
||||
}
|
||||
}
|
||||
|
||||
public int getNextIndex() {
|
||||
return this.nextIndex.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
super.clear();
|
||||
long stamp = this.keyToIndexLock.writeLock();
|
||||
|
||||
try {
|
||||
this.keyToIndex.clear();
|
||||
} finally {
|
||||
this.keyToIndexLock.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
|
||||
) {
|
||||
super.putAll(packKey, codec, loadedAssets, loadedKeyToPathMap, loadedAssetChildren);
|
||||
long stamp = this.keyToIndexLock.writeLock();
|
||||
|
||||
try {
|
||||
for (Entry<K, T> entry : loadedAssets.entrySet()) {
|
||||
K key = entry.getKey();
|
||||
int index;
|
||||
if ((index = this.keyToIndex.getInt(key)) == Integer.MIN_VALUE) {
|
||||
this.keyToIndex.put(key, index = this.nextIndex.getAndIncrement());
|
||||
}
|
||||
|
||||
T value = entry.getValue();
|
||||
this.putAssetTag(codec, key, index, value);
|
||||
}
|
||||
} finally {
|
||||
this.keyToIndexLock.unlockWrite(stamp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<K> remove(@Nonnull Set<K> keys) {
|
||||
Set<K> remove = super.remove(keys);
|
||||
this.remove0(keys);
|
||||
return remove;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<K> remove(@Nonnull String packKey, @Nonnull Set<K> keys, @Nonnull List<Entry<String, Object>> pathsToReload) {
|
||||
Set<K> remove = super.remove(packKey, keys, pathsToReload);
|
||||
this.remove0(keys);
|
||||
return remove;
|
||||
}
|
||||
|
||||
private void remove0(@Nonnull Set<K> keys) {
|
||||
long stamp = this.keyToIndexLock.writeLock();
|
||||
|
||||
try {
|
||||
for (K key : keys) {
|
||||
int index = this.keyToIndex.removeInt(key);
|
||||
this.indexedTagStorage.forEachWithInt((_k, value, idx) -> value.remove(idx), index);
|
||||
}
|
||||
} finally {
|
||||
this.keyToIndexLock.unlockWrite(stamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
package com.hypixel.hytale.assetstore.map;
|
||||
|
||||
import com.hypixel.hytale.assetstore.codec.AssetCodec;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
import java.util.function.IntFunction;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class IndexedLookupTableAssetMap<K, T extends JsonAssetWithMap<K, IndexedLookupTableAssetMap<K, T>>> extends AssetMapWithIndexes<K, T> {
|
||||
private final AtomicInteger nextIndex = new AtomicInteger();
|
||||
private final StampedLock keyToIndexLock = new StampedLock();
|
||||
private final Object2IntMap<K> keyToIndex = new Object2IntOpenCustomHashMap<>(CaseInsensitiveHashStrategy.getInstance());
|
||||
@Nonnull
|
||||
private final IntFunction<T[]> arrayProvider;
|
||||
private final ReentrantLock arrayLock = new ReentrantLock();
|
||||
private T[] array;
|
||||
|
||||
public IndexedLookupTableAssetMap(@Nonnull IntFunction<T[]> arrayProvider) {
|
||||
this.arrayProvider = arrayProvider;
|
||||
this.array = (T[])((JsonAssetWithMap[])arrayProvider.apply(0));
|
||||
this.keyToIndex.defaultReturnValue(Integer.MIN_VALUE);
|
||||
}
|
||||
|
||||
public int getIndex(K key) {
|
||||
long stamp = this.keyToIndexLock.tryOptimisticRead();
|
||||
int value = this.keyToIndex.getInt(key);
|
||||
if (this.keyToIndexLock.validate(stamp)) {
|
||||
return value;
|
||||
} else {
|
||||
stamp = this.keyToIndexLock.readLock();
|
||||
|
||||
int var5;
|
||||
try {
|
||||
var5 = this.keyToIndex.getInt(key);
|
||||
} finally {
|
||||
this.keyToIndexLock.unlockRead(stamp);
|
||||
}
|
||||
|
||||
return var5;
|
||||
}
|
||||
}
|
||||
|
||||
public int getIndexOrDefault(K key, int def) {
|
||||
long stamp = this.keyToIndexLock.tryOptimisticRead();
|
||||
int value = this.keyToIndex.getOrDefault(key, def);
|
||||
if (this.keyToIndexLock.validate(stamp)) {
|
||||
return value;
|
||||
} else {
|
||||
stamp = this.keyToIndexLock.readLock();
|
||||
|
||||
int var6;
|
||||
try {
|
||||
var6 = this.keyToIndex.getOrDefault(key, def);
|
||||
} finally {
|
||||
this.keyToIndexLock.unlockRead(stamp);
|
||||
}
|
||||
|
||||
return var6;
|
||||
}
|
||||
}
|
||||
|
||||
public int getNextIndex() {
|
||||
return this.nextIndex.get();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T getAsset(int index) {
|
||||
return index >= 0 && index < this.array.length ? this.array[index] : null;
|
||||
}
|
||||
|
||||
public T getAssetOrDefault(int index, T def) {
|
||||
return index >= 0 && index < this.array.length ? this.array[index] : def;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
super.clear();
|
||||
long stamp = this.keyToIndexLock.writeLock();
|
||||
this.arrayLock.lock();
|
||||
|
||||
try {
|
||||
this.keyToIndex.clear();
|
||||
this.array = (T[])((JsonAssetWithMap[])this.arrayProvider.apply(0));
|
||||
} finally {
|
||||
this.arrayLock.unlock();
|
||||
this.keyToIndexLock.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
|
||||
) {
|
||||
super.putAll(packKey, codec, loadedAssets, loadedKeyToPathMap, loadedAssetChildren);
|
||||
this.putAll0(codec, loadedAssets);
|
||||
}
|
||||
|
||||
private void putAll0(@Nonnull AssetCodec<K, T> codec, @Nonnull Map<K, T> loadedAssets) {
|
||||
long stamp = this.keyToIndexLock.writeLock();
|
||||
this.arrayLock.lock();
|
||||
|
||||
try {
|
||||
int highestIndex = 0;
|
||||
|
||||
for (K key : loadedAssets.keySet()) {
|
||||
int index = this.keyToIndex.getInt(key);
|
||||
if (index == Integer.MIN_VALUE) {
|
||||
this.keyToIndex.put(key, index = this.nextIndex.getAndIncrement());
|
||||
}
|
||||
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("Index can't be less than zero!");
|
||||
}
|
||||
|
||||
if (index > highestIndex) {
|
||||
highestIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
int length = highestIndex + 1;
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Highest index can't be less than zero!");
|
||||
}
|
||||
|
||||
if (length > this.array.length) {
|
||||
T[] newArray = (T[])((JsonAssetWithMap[])this.arrayProvider.apply(length));
|
||||
System.arraycopy(this.array, 0, newArray, 0, this.array.length);
|
||||
this.array = newArray;
|
||||
}
|
||||
|
||||
for (Entry<K, T> entry : loadedAssets.entrySet()) {
|
||||
K key = entry.getKey();
|
||||
int indexx = this.keyToIndex.getInt(key);
|
||||
if (indexx < 0) {
|
||||
throw new IllegalArgumentException("Index can't be less than zero!");
|
||||
}
|
||||
|
||||
T value = entry.getValue();
|
||||
this.array[indexx] = value;
|
||||
this.putAssetTag(codec, key, indexx, value);
|
||||
}
|
||||
} finally {
|
||||
this.arrayLock.unlock();
|
||||
this.keyToIndexLock.unlockWrite(stamp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<K> remove(@Nonnull Set<K> keys) {
|
||||
Set<K> remove = super.remove(keys);
|
||||
this.remove0(keys);
|
||||
return remove;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<K> remove(@Nonnull String packKey, @Nonnull Set<K> keys, @Nonnull List<Entry<String, Object>> pathsToReload) {
|
||||
Set<K> remove = super.remove(packKey, keys, pathsToReload);
|
||||
this.remove0(keys);
|
||||
return remove;
|
||||
}
|
||||
|
||||
private void remove0(@Nonnull Set<K> keys) {
|
||||
long stamp = this.keyToIndexLock.writeLock();
|
||||
this.arrayLock.lock();
|
||||
|
||||
try {
|
||||
for (K key : keys) {
|
||||
int blockId = this.keyToIndex.getInt(key);
|
||||
if (blockId != Integer.MIN_VALUE) {
|
||||
this.array[blockId] = null;
|
||||
this.indexedTagStorage.forEachWithInt((_k, value, id) -> value.remove(id), blockId);
|
||||
}
|
||||
}
|
||||
|
||||
int i = this.array.length - 1;
|
||||
|
||||
while (i > 0 && this.array[i] == null) {
|
||||
i--;
|
||||
}
|
||||
|
||||
int length = i + 1;
|
||||
if (length != this.array.length) {
|
||||
T[] newArray = (T[])((JsonAssetWithMap[])this.arrayProvider.apply(length));
|
||||
System.arraycopy(this.array, 0, newArray, 0, newArray.length);
|
||||
this.array = newArray;
|
||||
}
|
||||
} finally {
|
||||
this.arrayLock.unlock();
|
||||
this.keyToIndexLock.unlockWrite(stamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.hypixel.hytale.assetstore.map;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetMap;
|
||||
import com.hypixel.hytale.assetstore.JsonAsset;
|
||||
|
||||
public interface JsonAssetWithMap<K, M extends AssetMap<K, ?>> extends JsonAsset<K> {
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
package com.hypixel.hytale.assetstore.map;
|
||||
|
||||
import com.hypixel.hytale.assetstore.codec.AssetCodec;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.function.ToIntFunction;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class LookupTableAssetMap<K, T extends JsonAssetWithMap<K, LookupTableAssetMap<K, T>>> extends AssetMapWithIndexes<K, T> {
|
||||
@Nonnull
|
||||
private final IntFunction<T[]> arrayProvider;
|
||||
private final ToIntFunction<K> indexGetter;
|
||||
private final IntSupplier maxIndexGetter;
|
||||
private final ReentrantLock arrayLock = new ReentrantLock();
|
||||
private T[] array;
|
||||
|
||||
public LookupTableAssetMap(@Nonnull IntFunction<T[]> arrayProvider, ToIntFunction<K> indexGetter, IntSupplier maxIndexGetter) {
|
||||
this.arrayProvider = arrayProvider;
|
||||
this.indexGetter = indexGetter;
|
||||
this.maxIndexGetter = maxIndexGetter;
|
||||
this.array = (T[])((JsonAssetWithMap[])arrayProvider.apply(0));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T getAsset(int index) {
|
||||
return index >= 0 && index < this.array.length ? this.array[index] : null;
|
||||
}
|
||||
|
||||
public T getAssetOrDefault(int index, T def) {
|
||||
return index >= 0 && index < this.array.length ? this.array[index] : def;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
super.clear();
|
||||
this.arrayLock.lock();
|
||||
|
||||
try {
|
||||
this.array = (T[])((JsonAssetWithMap[])this.arrayProvider.apply(0));
|
||||
} finally {
|
||||
this.arrayLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
) {
|
||||
super.putAll(packKey, codec, loadedAssets, loadedKeyToPathMap, loadedAssetChildren);
|
||||
this.arrayLock.lock();
|
||||
|
||||
try {
|
||||
this.resize();
|
||||
|
||||
for (Entry<K, T> entry : loadedAssets.entrySet()) {
|
||||
K key = entry.getKey();
|
||||
int index = this.indexGetter.applyAsInt(key);
|
||||
if (index < 0) {
|
||||
throw new IllegalArgumentException("Index can't be less than zero!");
|
||||
}
|
||||
|
||||
if (index >= this.array.length) {
|
||||
throw new IllegalArgumentException("Index can't be higher than the max index!");
|
||||
}
|
||||
|
||||
T value = entry.getValue();
|
||||
this.array[index] = value;
|
||||
this.putAssetTag(codec, key, index, value);
|
||||
}
|
||||
} finally {
|
||||
this.arrayLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<K> remove(@Nonnull Set<K> keys) {
|
||||
Set<K> remove = super.remove(keys);
|
||||
this.remove0(keys);
|
||||
return remove;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<K> remove(@Nonnull String packKey, @Nonnull Set<K> keys, @Nonnull List<Entry<String, Object>> pathsToReload) {
|
||||
Set<K> remove = super.remove(packKey, keys, pathsToReload);
|
||||
this.remove0(keys);
|
||||
return remove;
|
||||
}
|
||||
|
||||
private void remove0(@Nonnull Set<K> keys) {
|
||||
this.arrayLock.lock();
|
||||
|
||||
try {
|
||||
for (K key : keys) {
|
||||
int blockId = this.indexGetter.applyAsInt(key);
|
||||
if (blockId != Integer.MIN_VALUE) {
|
||||
this.array[blockId] = null;
|
||||
this.indexedTagStorage.forEachWithInt((_k, value, id) -> value.remove(id), blockId);
|
||||
}
|
||||
}
|
||||
|
||||
this.resize();
|
||||
} finally {
|
||||
this.arrayLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void resize() {
|
||||
int length = this.maxIndexGetter.getAsInt();
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("max index can't be less than zero!");
|
||||
} else {
|
||||
if (length > this.array.length) {
|
||||
T[] newArray = (T[])((JsonAssetWithMap[])this.arrayProvider.apply(length));
|
||||
System.arraycopy(this.array, 0, newArray, 0, this.array.length);
|
||||
this.array = newArray;
|
||||
} else if (length < this.array.length) {
|
||||
for (int i = length; i < this.array.length; i++) {
|
||||
if (this.array[i] != null) {
|
||||
throw new IllegalArgumentException("Assets exist in the array outside of the max index!");
|
||||
}
|
||||
}
|
||||
|
||||
T[] newArray = (T[])((JsonAssetWithMap[])this.arrayProvider.apply(length));
|
||||
System.arraycopy(this.array, 0, newArray, 0, newArray.length);
|
||||
this.array = newArray;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requireReplaceOnRemove() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.hypixel.hytale.assetstore.map;
|
||||
|
||||
import com.hypixel.hytale.assetstore.codec.AssetCodec;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenCustomHashMap;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
import java.util.function.ToIntBiFunction;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class ProvidedIndexAssetMap<K, T extends JsonAssetWithMap<K, ProvidedIndexAssetMap<K, T>>> extends AssetMapWithIndexes<K, T> {
|
||||
private final StampedLock keyToIndexLock = new StampedLock();
|
||||
private final Object2IntMap<K> keyToIndex = new Object2IntOpenCustomHashMap<>(CaseInsensitiveHashStrategy.getInstance());
|
||||
private final ToIntBiFunction<K, T> indexGetter;
|
||||
|
||||
public ProvidedIndexAssetMap(ToIntBiFunction<K, T> indexGetter) {
|
||||
this.indexGetter = indexGetter;
|
||||
this.keyToIndex.defaultReturnValue(Integer.MIN_VALUE);
|
||||
}
|
||||
|
||||
public int getIndex(K key) {
|
||||
long stamp = this.keyToIndexLock.tryOptimisticRead();
|
||||
int value = this.keyToIndex.getInt(key);
|
||||
if (this.keyToIndexLock.validate(stamp)) {
|
||||
return value;
|
||||
} else {
|
||||
stamp = this.keyToIndexLock.readLock();
|
||||
|
||||
int var5;
|
||||
try {
|
||||
var5 = this.keyToIndex.getInt(key);
|
||||
} finally {
|
||||
this.keyToIndexLock.unlockRead(stamp);
|
||||
}
|
||||
|
||||
return var5;
|
||||
}
|
||||
}
|
||||
|
||||
public int getIndexOrDefault(K key, int def) {
|
||||
long stamp = this.keyToIndexLock.tryOptimisticRead();
|
||||
int value = this.keyToIndex.getOrDefault(key, def);
|
||||
if (this.keyToIndexLock.validate(stamp)) {
|
||||
return value;
|
||||
} else {
|
||||
stamp = this.keyToIndexLock.readLock();
|
||||
|
||||
int var6;
|
||||
try {
|
||||
var6 = this.keyToIndex.getOrDefault(key, def);
|
||||
} finally {
|
||||
this.keyToIndexLock.unlockRead(stamp);
|
||||
}
|
||||
|
||||
return var6;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void clear() {
|
||||
super.clear();
|
||||
long stamp = this.keyToIndexLock.writeLock();
|
||||
|
||||
try {
|
||||
this.keyToIndex.clear();
|
||||
} finally {
|
||||
this.keyToIndexLock.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
|
||||
) {
|
||||
super.putAll(packKey, codec, loadedAssets, loadedKeyToPathMap, loadedAssetChildren);
|
||||
long stamp = this.keyToIndexLock.writeLock();
|
||||
|
||||
try {
|
||||
for (Entry<K, T> entry : loadedAssets.entrySet()) {
|
||||
K key = entry.getKey();
|
||||
T value = entry.getValue();
|
||||
int index;
|
||||
if ((index = this.keyToIndex.getInt(key)) == Integer.MIN_VALUE) {
|
||||
this.keyToIndex.put(key, index = this.indexGetter.applyAsInt(key, value));
|
||||
}
|
||||
|
||||
this.putAssetTag(codec, key, index, value);
|
||||
}
|
||||
} finally {
|
||||
this.keyToIndexLock.unlockWrite(stamp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<K> remove(@Nonnull Set<K> keys) {
|
||||
Set<K> remove = super.remove(keys);
|
||||
long stamp = this.keyToIndexLock.writeLock();
|
||||
|
||||
try {
|
||||
for (K key : keys) {
|
||||
int index = this.keyToIndex.removeInt(key);
|
||||
this.indexedTagStorage.forEachWithInt((_k, value, idx) -> value.remove(idx), index);
|
||||
}
|
||||
} finally {
|
||||
this.keyToIndexLock.unlockWrite(stamp);
|
||||
}
|
||||
|
||||
return remove;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requireReplaceOnRemove() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.hypixel.hytale.builtin.adventure.camera;
|
||||
|
||||
import com.hypixel.hytale.assetstore.map.DefaultAssetMap;
|
||||
import com.hypixel.hytale.assetstore.map.IndexedAssetMap;
|
||||
import com.hypixel.hytale.builtin.adventure.camera.asset.cameraeffect.CameraShakeEffect;
|
||||
import com.hypixel.hytale.builtin.adventure.camera.asset.camerashake.CameraShake;
|
||||
import com.hypixel.hytale.builtin.adventure.camera.asset.camerashake.CameraShakePacketGenerator;
|
||||
import com.hypixel.hytale.builtin.adventure.camera.asset.viewbobbing.ViewBobbing;
|
||||
import com.hypixel.hytale.builtin.adventure.camera.asset.viewbobbing.ViewBobbingPacketGenerator;
|
||||
import com.hypixel.hytale.builtin.adventure.camera.command.CameraEffectCommand;
|
||||
import com.hypixel.hytale.builtin.adventure.camera.interaction.CameraShakeInteraction;
|
||||
import com.hypixel.hytale.builtin.adventure.camera.system.CameraEffectSystem;
|
||||
import com.hypixel.hytale.protocol.MovementType;
|
||||
import com.hypixel.hytale.server.core.asset.HytaleAssetStore;
|
||||
import com.hypixel.hytale.server.core.asset.type.camera.CameraEffect;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
|
||||
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
|
||||
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
|
||||
import com.hypixel.hytale.server.core.plugin.registry.AssetRegistry;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class CameraPlugin extends JavaPlugin {
|
||||
private static final String CODEC_CAMERA_SHAKE = "CameraShake";
|
||||
|
||||
public CameraPlugin(@Nonnull JavaPluginInit init) {
|
||||
super(init);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setup() {
|
||||
AssetRegistry assetRegistry = this.getAssetRegistry();
|
||||
this.getCodecRegistry(CameraEffect.CODEC).register("CameraShake", CameraShakeEffect.class, CameraShakeEffect.CODEC);
|
||||
this.getCodecRegistry(Interaction.CODEC).register("CameraShake", CameraShakeInteraction.class, CameraShakeInteraction.CODEC);
|
||||
assetRegistry.register(
|
||||
((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder(
|
||||
String.class, CameraShake.class, new IndexedAssetMap()
|
||||
)
|
||||
.loadsBefore(CameraEffect.class))
|
||||
.setCodec(CameraShake.CODEC))
|
||||
.setPath("Camera/CameraShake"))
|
||||
.setKeyFunction(CameraShake::getId))
|
||||
.setReplaceOnRemove(CameraShake::new))
|
||||
.setPacketGenerator(new CameraShakePacketGenerator())
|
||||
.build()
|
||||
);
|
||||
assetRegistry.register(
|
||||
((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder(
|
||||
MovementType.class, ViewBobbing.class, new DefaultAssetMap()
|
||||
)
|
||||
.setCodec(ViewBobbing.CODEC))
|
||||
.setPath("Camera/ViewBobbing"))
|
||||
.setKeyFunction(ViewBobbing::getId))
|
||||
.setPacketGenerator(new ViewBobbingPacketGenerator())
|
||||
.build()
|
||||
);
|
||||
this.getCommandRegistry().registerCommand(new CameraEffectCommand());
|
||||
this.getEntityStoreRegistry().registerSystem(new CameraEffectSystem());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.hypixel.hytale.builtin.adventure.camera.asset;
|
||||
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.server.core.io.NetworkSerializable;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class CameraShakeConfig implements NetworkSerializable<com.hypixel.hytale.protocol.CameraShakeConfig> {
|
||||
public static final BuilderCodec<CameraShakeConfig> CODEC = BuilderCodec.builder(CameraShakeConfig.class, CameraShakeConfig::new)
|
||||
.appendInherited(new KeyedCodec<>("Duration", Codec.FLOAT), (o, v) -> o.duration = v, o -> o.duration, (o, p) -> o.duration = p.duration)
|
||||
.documentation("The time period that the camera will shake at full intensity for")
|
||||
.addValidator(Validators.min(0.0F))
|
||||
.add()
|
||||
.<Float>appendInherited(new KeyedCodec<>("StartTime", Codec.FLOAT), (o, v) -> o.startTime = v, o -> o.startTime, (o, p) -> o.startTime = p.startTime)
|
||||
.documentation(
|
||||
"The initial time value that the Offset and Rotation noises are sampled from when the camera-shake starts. If absent, the camera-shake uses a continuously incremented time value."
|
||||
)
|
||||
.add()
|
||||
.<EasingConfig>appendInherited(new KeyedCodec<>("EaseIn", EasingConfig.CODEC), (o, v) -> o.easeIn = v, o -> o.easeIn, (o, p) -> o.easeIn = p.easeIn)
|
||||
.documentation("The fade-in time and intensity curve for the camera shake")
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.<EasingConfig>appendInherited(new KeyedCodec<>("EaseOut", EasingConfig.CODEC), (o, v) -> o.easeOut = v, o -> o.easeOut, (o, p) -> o.easeOut = p.easeOut)
|
||||
.documentation("The fade-out time and intensity curve for the camera shake")
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.<CameraShakeConfig.OffsetNoise>appendInherited(
|
||||
new KeyedCodec<>("Offset", CameraShakeConfig.OffsetNoise.CODEC), (o, v) -> o.offset = v, o -> o.offset, (o, p) -> o.offset = p.offset
|
||||
)
|
||||
.documentation("The translational offset motion")
|
||||
.add()
|
||||
.<CameraShakeConfig.RotationNoise>appendInherited(
|
||||
new KeyedCodec<>("Rotation", CameraShakeConfig.RotationNoise.CODEC), (o, v) -> o.rotation = v, o -> o.rotation, (o, p) -> o.rotation = p.rotation
|
||||
)
|
||||
.documentation("The rotational motion")
|
||||
.add()
|
||||
.build();
|
||||
protected float duration;
|
||||
protected Float startTime;
|
||||
protected EasingConfig easeIn = EasingConfig.NONE;
|
||||
protected EasingConfig easeOut = EasingConfig.NONE;
|
||||
protected CameraShakeConfig.OffsetNoise offset = CameraShakeConfig.OffsetNoise.NONE;
|
||||
protected CameraShakeConfig.RotationNoise rotation = CameraShakeConfig.RotationNoise.NONE;
|
||||
|
||||
public CameraShakeConfig() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public com.hypixel.hytale.protocol.CameraShakeConfig toPacket() {
|
||||
boolean continuous = this.startTime == null;
|
||||
float startTime = continuous ? 0.0F : this.startTime;
|
||||
return new com.hypixel.hytale.protocol.CameraShakeConfig(
|
||||
this.duration, startTime, continuous, this.easeIn.toPacket(), this.easeOut.toPacket(), this.offset.toPacket(), this.rotation.toPacket()
|
||||
);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CameraShakeConfig{duration="
|
||||
+ this.duration
|
||||
+ ", startTime="
|
||||
+ this.startTime
|
||||
+ ", easeIn="
|
||||
+ this.easeIn
|
||||
+ ", easeOut="
|
||||
+ this.easeOut
|
||||
+ ", offset="
|
||||
+ this.offset
|
||||
+ ", rotation="
|
||||
+ this.rotation
|
||||
+ "}";
|
||||
}
|
||||
|
||||
public static class OffsetNoise implements NetworkSerializable<com.hypixel.hytale.protocol.OffsetNoise> {
|
||||
public static final BuilderCodec<CameraShakeConfig.OffsetNoise> CODEC = BuilderCodec.builder(
|
||||
CameraShakeConfig.OffsetNoise.class, CameraShakeConfig.OffsetNoise::new
|
||||
)
|
||||
.documentation(
|
||||
"The translational offset noise sources. Each component's list of noise configurations are summed together to calculate the output value for that component"
|
||||
)
|
||||
.<NoiseConfig[]>appendInherited(new KeyedCodec<>("X", NoiseConfig.ARRAY_CODEC), (o, v) -> o.x = v, o -> o.x, (o, p) -> o.x = p.x)
|
||||
.documentation("The noise used to vary the camera x-offset")
|
||||
.add()
|
||||
.<NoiseConfig[]>appendInherited(new KeyedCodec<>("Y", NoiseConfig.ARRAY_CODEC), (o, v) -> o.y = v, o -> o.y, (o, p) -> o.y = p.y)
|
||||
.documentation("The noise used to vary the camera y-offset")
|
||||
.add()
|
||||
.<NoiseConfig[]>appendInherited(new KeyedCodec<>("Z", NoiseConfig.ARRAY_CODEC), (o, v) -> o.z = v, o -> o.z, (o, p) -> o.z = p.z)
|
||||
.documentation("The noise used to vary the camera z-offset")
|
||||
.add()
|
||||
.build();
|
||||
public static final CameraShakeConfig.OffsetNoise NONE = new CameraShakeConfig.OffsetNoise();
|
||||
protected NoiseConfig[] x;
|
||||
protected NoiseConfig[] y;
|
||||
protected NoiseConfig[] z;
|
||||
|
||||
public OffsetNoise() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public com.hypixel.hytale.protocol.OffsetNoise toPacket() {
|
||||
return new com.hypixel.hytale.protocol.OffsetNoise(NoiseConfig.toPacket(this.x), NoiseConfig.toPacket(this.y), NoiseConfig.toPacket(this.z));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "OffsetNoise{x="
|
||||
+ Arrays.toString((Object[])this.x)
|
||||
+ ", y="
|
||||
+ Arrays.toString((Object[])this.y)
|
||||
+ ", z="
|
||||
+ Arrays.toString((Object[])this.z)
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
|
||||
public static class RotationNoise implements NetworkSerializable<com.hypixel.hytale.protocol.RotationNoise> {
|
||||
public static final BuilderCodec<CameraShakeConfig.RotationNoise> CODEC = BuilderCodec.builder(
|
||||
CameraShakeConfig.RotationNoise.class, CameraShakeConfig.RotationNoise::new
|
||||
)
|
||||
.documentation(
|
||||
"The rotational noise sources. Each component's list of noise configurations are summed together to calculate the output value for that component"
|
||||
)
|
||||
.<NoiseConfig[]>appendInherited(new KeyedCodec<>("Pitch", NoiseConfig.ARRAY_CODEC), (o, v) -> o.pitch = v, o -> o.pitch, (o, p) -> o.pitch = p.pitch)
|
||||
.documentation("The noise used to vary the camera pitch")
|
||||
.add()
|
||||
.<NoiseConfig[]>appendInherited(new KeyedCodec<>("Yaw", NoiseConfig.ARRAY_CODEC), (o, v) -> o.yaw = v, o -> o.yaw, (o, p) -> o.yaw = p.yaw)
|
||||
.documentation("The noise used to vary the camera yaw")
|
||||
.add()
|
||||
.<NoiseConfig[]>appendInherited(new KeyedCodec<>("Roll", NoiseConfig.ARRAY_CODEC), (o, v) -> o.roll = v, o -> o.roll, (o, p) -> o.roll = p.roll)
|
||||
.documentation("The noise used to vary the camera roll")
|
||||
.add()
|
||||
.build();
|
||||
public static final CameraShakeConfig.RotationNoise NONE = new CameraShakeConfig.RotationNoise();
|
||||
protected NoiseConfig[] pitch;
|
||||
protected NoiseConfig[] yaw;
|
||||
protected NoiseConfig[] roll;
|
||||
|
||||
public RotationNoise() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public com.hypixel.hytale.protocol.RotationNoise toPacket() {
|
||||
return new com.hypixel.hytale.protocol.RotationNoise(NoiseConfig.toPacket(this.pitch), NoiseConfig.toPacket(this.yaw), NoiseConfig.toPacket(this.roll));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RotationNoise{pitch="
|
||||
+ Arrays.toString((Object[])this.pitch)
|
||||
+ ", yaw="
|
||||
+ Arrays.toString((Object[])this.yaw)
|
||||
+ ", roll="
|
||||
+ Arrays.toString((Object[])this.roll)
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.hypixel.hytale.builtin.adventure.camera.asset;
|
||||
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.protocol.EasingType;
|
||||
import com.hypixel.hytale.server.core.codec.ProtocolCodecs;
|
||||
import com.hypixel.hytale.server.core.io.NetworkSerializable;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class EasingConfig implements NetworkSerializable<com.hypixel.hytale.protocol.EasingConfig> {
|
||||
@Nonnull
|
||||
public static final BuilderCodec<EasingConfig> CODEC = BuilderCodec.builder(EasingConfig.class, EasingConfig::new)
|
||||
.appendInherited(new KeyedCodec<>("Time", Codec.FLOAT), (o, v) -> o.time = v, o -> o.time, (o, p) -> o.time = p.time)
|
||||
.documentation("The duration time of the easing")
|
||||
.addValidator(Validators.min(0.0F))
|
||||
.add()
|
||||
.<EasingType>appendInherited(new KeyedCodec<>("Type", ProtocolCodecs.EASING_TYPE_CODEC), (o, v) -> o.type = v, o -> o.type, (o, p) -> o.type = p.type)
|
||||
.documentation("The curve type of the easing")
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.build();
|
||||
@Nonnull
|
||||
public static final EasingConfig NONE = new EasingConfig();
|
||||
protected float time;
|
||||
@Nonnull
|
||||
protected EasingType type = EasingType.Linear;
|
||||
|
||||
public EasingConfig() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public com.hypixel.hytale.protocol.EasingConfig toPacket() {
|
||||
return new com.hypixel.hytale.protocol.EasingConfig(this.time, this.type);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "EasingConfig{time=" + this.time + ", type=" + this.type + "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.hypixel.hytale.builtin.adventure.camera.asset;
|
||||
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.EnumCodec;
|
||||
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
|
||||
import com.hypixel.hytale.codec.schema.metadata.ui.UIDefaultCollapsedState;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.protocol.NoiseType;
|
||||
import com.hypixel.hytale.server.core.io.NetworkSerializable;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class NoiseConfig implements NetworkSerializable<com.hypixel.hytale.protocol.NoiseConfig> {
|
||||
@Nonnull
|
||||
public static final Codec<NoiseType> NOISE_TYPE_CODEC = new EnumCodec<>(NoiseType.class);
|
||||
@Nonnull
|
||||
public static final BuilderCodec<NoiseConfig> CODEC = BuilderCodec.builder(NoiseConfig.class, NoiseConfig::new)
|
||||
.appendInherited(new KeyedCodec<>("Seed", Codec.INTEGER), (o, v) -> o.seed = v, o -> o.seed, (o, p) -> o.seed = p.seed)
|
||||
.documentation("The value used to seed the noise source")
|
||||
.add()
|
||||
.<NoiseType>appendInherited(new KeyedCodec<>("Type", NOISE_TYPE_CODEC), (o, v) -> o.type = v, o -> o.type, (o, p) -> o.type = p.type)
|
||||
.documentation("The type of noise used to move the camera")
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.<Float>appendInherited(new KeyedCodec<>("Frequency", Codec.FLOAT), (o, v) -> o.frequency = v, o -> o.frequency, (o, p) -> o.frequency = p.frequency)
|
||||
.documentation("The frequency at which the noise source is sampled")
|
||||
.add()
|
||||
.<Float>appendInherited(new KeyedCodec<>("Amplitude", Codec.FLOAT), (o, v) -> o.amplitude = v, o -> o.amplitude, (o, p) -> o.amplitude = p.amplitude)
|
||||
.documentation("The maximum extent of the noise source output")
|
||||
.add()
|
||||
.<NoiseConfig.ClampConfig>appendInherited(
|
||||
new KeyedCodec<>("Clamp", NoiseConfig.ClampConfig.CODEC), (o, v) -> o.clamp = v, o -> o.clamp, (o, p) -> o.clamp = p.clamp
|
||||
)
|
||||
.documentation("Restricts the range of values that the noise source can output")
|
||||
.metadata(UIDefaultCollapsedState.UNCOLLAPSED)
|
||||
.add()
|
||||
.build();
|
||||
@Nonnull
|
||||
public static final ArrayCodec<NoiseConfig> ARRAY_CODEC = ArrayCodec.ofBuilderCodec(CODEC, NoiseConfig[]::new);
|
||||
@Nonnull
|
||||
public static final com.hypixel.hytale.protocol.NoiseConfig[] NOISE_CONFIGS = new com.hypixel.hytale.protocol.NoiseConfig[0];
|
||||
protected int seed;
|
||||
@Nonnull
|
||||
protected NoiseType type = NoiseType.Sin;
|
||||
@Nonnull
|
||||
protected NoiseConfig.ClampConfig clamp = NoiseConfig.ClampConfig.NONE;
|
||||
protected float frequency;
|
||||
protected float amplitude;
|
||||
|
||||
public NoiseConfig() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public com.hypixel.hytale.protocol.NoiseConfig toPacket() {
|
||||
return new com.hypixel.hytale.protocol.NoiseConfig(this.seed, this.type, this.frequency, this.amplitude, this.clamp.toPacket());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NoiseConfig{seed="
|
||||
+ this.seed
|
||||
+ ", type="
|
||||
+ this.type
|
||||
+ ", clamp="
|
||||
+ this.clamp
|
||||
+ ", frequency="
|
||||
+ this.frequency
|
||||
+ ", amplitude="
|
||||
+ this.amplitude
|
||||
+ "}";
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static com.hypixel.hytale.protocol.NoiseConfig[] toPacket(@Nullable NoiseConfig[] configs) {
|
||||
if (configs != null && configs.length != 0) {
|
||||
com.hypixel.hytale.protocol.NoiseConfig[] result = new com.hypixel.hytale.protocol.NoiseConfig[configs.length];
|
||||
|
||||
for (int i = 0; i < configs.length; i++) {
|
||||
NoiseConfig config = configs[i];
|
||||
if (config != null) {
|
||||
result[i] = config.toPacket();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
} else {
|
||||
return NOISE_CONFIGS;
|
||||
}
|
||||
}
|
||||
|
||||
public static class ClampConfig implements NetworkSerializable<com.hypixel.hytale.protocol.ClampConfig> {
|
||||
@Nonnull
|
||||
public static final BuilderCodec<NoiseConfig.ClampConfig> CODEC = BuilderCodec.builder(NoiseConfig.ClampConfig.class, NoiseConfig.ClampConfig::new)
|
||||
.appendInherited(new KeyedCodec<>("Min", Codec.FLOAT), (o, v) -> o.min = v, o -> o.min, (o, p) -> o.min = p.min)
|
||||
.documentation("The inclusive minimum value of the clamp range")
|
||||
.addValidator(Validators.range(-1.0F, 1.0F))
|
||||
.add()
|
||||
.<Float>appendInherited(new KeyedCodec<>("Max", Codec.FLOAT), (o, v) -> o.max = v, o -> o.max, (o, p) -> o.max = p.max)
|
||||
.documentation("The inclusive maximum value of the clamp range")
|
||||
.addValidator(Validators.range(-1.0F, 1.0F))
|
||||
.add()
|
||||
.<Boolean>appendInherited(
|
||||
new KeyedCodec<>("Normalize", Codec.BOOLEAN), (o, v) -> o.normalize = v, o -> o.normalize, (o, p) -> o.normalize = p.normalize
|
||||
)
|
||||
.documentation("Rescales the clamped output value back to the range -1 to 1")
|
||||
.add()
|
||||
.afterDecode(range -> {
|
||||
range.min = Math.min(range.min, range.max);
|
||||
range.max = Math.max(range.min, range.max);
|
||||
})
|
||||
.build();
|
||||
@Nonnull
|
||||
public static final NoiseConfig.ClampConfig NONE = new NoiseConfig.ClampConfig();
|
||||
protected float min = -1.0F;
|
||||
protected float max = 1.0F;
|
||||
protected boolean normalize = true;
|
||||
|
||||
public ClampConfig() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public com.hypixel.hytale.protocol.ClampConfig toPacket() {
|
||||
return new com.hypixel.hytale.protocol.ClampConfig(this.min, this.max, this.normalize);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ClampConfig{min=" + this.min + ", max=" + this.max + ", normalize=" + this.normalize + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.hypixel.hytale.builtin.adventure.camera.asset.cameraeffect;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.camera.asset.camerashake.CameraShake;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.protocol.AccumulationMode;
|
||||
import com.hypixel.hytale.server.core.asset.type.camera.CameraEffect;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class CameraShakeEffect extends CameraEffect {
|
||||
@Nonnull
|
||||
public static final BuilderCodec<CameraShakeEffect> CODEC = BuilderCodec.builder(CameraShakeEffect.class, CameraShakeEffect::new)
|
||||
.appendInherited(
|
||||
new KeyedCodec<>("CameraShake", CameraShake.CHILD_ASSET_CODEC),
|
||||
(cameraShakeEffect, s) -> cameraShakeEffect.cameraShakeId = s,
|
||||
cameraShakeEffect -> cameraShakeEffect.cameraShakeId,
|
||||
(cameraShakeEffect, parent) -> cameraShakeEffect.cameraShakeId = parent.cameraShakeId
|
||||
)
|
||||
.documentation("The type of camera shake to apply for this effect.")
|
||||
.addValidator(CameraShake.VALIDATOR_CACHE.getValidator())
|
||||
.add()
|
||||
.<ShakeIntensity>appendInherited(
|
||||
new KeyedCodec<>("Intensity", ShakeIntensity.CODEC),
|
||||
(cameraShakeEffect, s) -> cameraShakeEffect.intensity = s,
|
||||
cameraShakeEffect -> cameraShakeEffect.intensity,
|
||||
(cameraShakeEffect, parent) -> cameraShakeEffect.intensity = parent.intensity
|
||||
)
|
||||
.documentation("Controls how intensity-context (such as damage) is interpreted as shake intensity.")
|
||||
.add()
|
||||
.afterDecode(cameraShakeEffect -> {
|
||||
if (cameraShakeEffect.cameraShakeId != null) {
|
||||
cameraShakeEffect.cameraShakeIndex = CameraShake.getAssetMap().getIndex(cameraShakeEffect.cameraShakeId);
|
||||
}
|
||||
})
|
||||
.build();
|
||||
@Nullable
|
||||
protected String cameraShakeId;
|
||||
protected int cameraShakeIndex = Integer.MIN_VALUE;
|
||||
@Nullable
|
||||
protected ShakeIntensity intensity;
|
||||
|
||||
public CameraShakeEffect() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public AccumulationMode getAccumulationMode() {
|
||||
return this.intensity == null ? ShakeIntensity.DEFAULT_ACCUMULATION_MODE : this.intensity.getAccumulationMode();
|
||||
}
|
||||
|
||||
public float getDefaultIntensityContext() {
|
||||
return this.intensity == null ? 0.0F : this.intensity.getValue();
|
||||
}
|
||||
|
||||
public float calculateIntensity(float intensityContext) {
|
||||
if (this.intensity == null) {
|
||||
return intensityContext;
|
||||
} else {
|
||||
ShakeIntensity.Modifier modifier = this.intensity.getModifier();
|
||||
return modifier == null ? intensityContext : modifier.apply(intensityContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public com.hypixel.hytale.protocol.packets.camera.CameraShakeEffect createCameraShakePacket() {
|
||||
float intensity = this.getDefaultIntensityContext();
|
||||
return this.createCameraShakePacket(intensity);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public com.hypixel.hytale.protocol.packets.camera.CameraShakeEffect createCameraShakePacket(float intensityContext) {
|
||||
float intensity = this.calculateIntensity(intensityContext);
|
||||
AccumulationMode accumulationMode = this.getAccumulationMode();
|
||||
return new com.hypixel.hytale.protocol.packets.camera.CameraShakeEffect(this.cameraShakeIndex, intensity, accumulationMode);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CameraShakeEffect{id='"
|
||||
+ this.id
|
||||
+ "', data="
|
||||
+ this.data
|
||||
+ ", cameraShakeId='"
|
||||
+ this.cameraShakeId
|
||||
+ "', cameraShakeIndex="
|
||||
+ this.cameraShakeIndex
|
||||
+ ", intensity="
|
||||
+ this.intensity
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.hypixel.hytale.builtin.adventure.camera.asset.cameraeffect;
|
||||
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.math.util.MathUtil;
|
||||
import com.hypixel.hytale.protocol.AccumulationMode;
|
||||
import com.hypixel.hytale.server.core.codec.ProtocolCodecs;
|
||||
import it.unimi.dsi.fastutil.floats.FloatUnaryOperator;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ShakeIntensity {
|
||||
@Nonnull
|
||||
public static final BuilderCodec<ShakeIntensity> CODEC = BuilderCodec.builder(ShakeIntensity.class, ShakeIntensity::new)
|
||||
.appendInherited(
|
||||
new KeyedCodec<>("Value", Codec.FLOAT),
|
||||
(cameraShakeEffect, s) -> cameraShakeEffect.value = s,
|
||||
cameraShakeEffect -> cameraShakeEffect.value,
|
||||
(cameraShakeEffect, parent) -> cameraShakeEffect.value = parent.value
|
||||
)
|
||||
.documentation("The intensity used when no contextual value (such as damage) is present.")
|
||||
.addValidator(Validators.range(0.0F, 1.0F))
|
||||
.add()
|
||||
.<AccumulationMode>appendInherited(
|
||||
new KeyedCodec<>("AccumulationMode", ProtocolCodecs.ACCUMULATION_MODE_CODEC),
|
||||
(intensity, mode) -> intensity.accumulationMode = mode,
|
||||
intensity -> intensity.accumulationMode,
|
||||
(intensity, parent) -> intensity.accumulationMode = parent.accumulationMode
|
||||
)
|
||||
.documentation("The method by which intensity is combined when multiple instances of the same camera effect overlap.")
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.<ShakeIntensity.Modifier>appendInherited(
|
||||
new KeyedCodec<>("Modifier", ShakeIntensity.Modifier.CODEC),
|
||||
(intensity, modifier) -> intensity.modifier = modifier,
|
||||
intensity -> intensity.modifier,
|
||||
(intensity, parent) -> intensity.modifier = parent.modifier
|
||||
)
|
||||
.documentation("Converts a contextual-intensity value (such as damage) to a camera shake intensity value.")
|
||||
.add()
|
||||
.build();
|
||||
@Nonnull
|
||||
static final AccumulationMode DEFAULT_ACCUMULATION_MODE = AccumulationMode.Set;
|
||||
public static final float DEFAULT_CONTEXT_VALUE = 0.0F;
|
||||
protected float value = 0.0F;
|
||||
@Nonnull
|
||||
protected AccumulationMode accumulationMode = DEFAULT_ACCUMULATION_MODE;
|
||||
@Nullable
|
||||
protected ShakeIntensity.Modifier modifier;
|
||||
|
||||
public ShakeIntensity() {
|
||||
}
|
||||
|
||||
public float getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public AccumulationMode getAccumulationMode() {
|
||||
return this.accumulationMode;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ShakeIntensity.Modifier getModifier() {
|
||||
return this.modifier;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ShakeIntensity{value=" + this.value + ", accumulationMode=" + this.accumulationMode + ", modifier=" + this.modifier + "}";
|
||||
}
|
||||
|
||||
public static class Modifier implements FloatUnaryOperator {
|
||||
@Nonnull
|
||||
public static final BuilderCodec<ShakeIntensity.Modifier> CODEC = BuilderCodec.builder(ShakeIntensity.Modifier.class, ShakeIntensity.Modifier::new)
|
||||
.appendInherited(
|
||||
new KeyedCodec<>("Input", Codec.FLOAT_ARRAY),
|
||||
(modifier, v) -> modifier.input = v,
|
||||
modifier -> modifier.input,
|
||||
(modifier, parent) -> modifier.input = parent.input
|
||||
)
|
||||
.addValidator(Validators.nonEmptyFloatArray())
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.<float[]>appendInherited(
|
||||
new KeyedCodec<>("Output", Codec.FLOAT_ARRAY),
|
||||
(modifier, v) -> modifier.output = v,
|
||||
modifier -> modifier.output,
|
||||
(modifier, parent) -> modifier.output = parent.output
|
||||
)
|
||||
.addValidator(Validators.nonEmptyFloatArray())
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.build();
|
||||
private float[] input;
|
||||
private float[] output;
|
||||
|
||||
public Modifier() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public float apply(float intensityContext) {
|
||||
float inputMin = this.input[0];
|
||||
float outputMin = this.output[0];
|
||||
if (intensityContext < inputMin) {
|
||||
return outputMin;
|
||||
} else {
|
||||
int length = Math.min(this.input.length, this.output.length);
|
||||
|
||||
for (int i = 1; i < length; i++) {
|
||||
float inputMax = this.input[i];
|
||||
float outputMax = this.output[i];
|
||||
if (!(intensityContext > inputMax)) {
|
||||
return MathUtil.mapToRange(intensityContext, inputMin, inputMax, outputMin, outputMax);
|
||||
}
|
||||
|
||||
inputMin = inputMax;
|
||||
outputMin = outputMax;
|
||||
}
|
||||
|
||||
return outputMin;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Modifier{input=" + Arrays.toString(this.input) + ", output=" + Arrays.toString(this.output) + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.hypixel.hytale.builtin.adventure.camera.asset.camerashake;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetExtraInfo;
|
||||
import com.hypixel.hytale.assetstore.AssetKeyValidator;
|
||||
import com.hypixel.hytale.assetstore.AssetRegistry;
|
||||
import com.hypixel.hytale.assetstore.AssetStore;
|
||||
import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec;
|
||||
import com.hypixel.hytale.assetstore.codec.ContainedAssetCodec;
|
||||
import com.hypixel.hytale.assetstore.map.IndexedAssetMap;
|
||||
import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;
|
||||
import com.hypixel.hytale.builtin.adventure.camera.asset.CameraShakeConfig;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.validation.ValidatorCache;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.server.core.io.NetworkSerializable;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class CameraShake implements NetworkSerializable<com.hypixel.hytale.protocol.CameraShake>, JsonAssetWithMap<String, IndexedAssetMap<String, CameraShake>> {
|
||||
@Nonnull
|
||||
public static final AssetBuilderCodec<String, CameraShake> CODEC = AssetBuilderCodec.builder(
|
||||
CameraShake.class, CameraShake::new, Codec.STRING, (o, v) -> o.id = v, CameraShake::getId, (o, data) -> o.data = data, o -> o.data
|
||||
)
|
||||
.appendInherited(
|
||||
new KeyedCodec<>("FirstPerson", CameraShakeConfig.CODEC), (o, v) -> o.firstPerson = v, o -> o.firstPerson, (o, p) -> o.firstPerson = p.firstPerson
|
||||
)
|
||||
.documentation("The camera shake to apply to the first-person camera")
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.<CameraShakeConfig>appendInherited(
|
||||
new KeyedCodec<>("ThirdPerson", CameraShakeConfig.CODEC), (o, v) -> o.thirdPerson = v, o -> o.thirdPerson, (o, p) -> o.thirdPerson = p.thirdPerson
|
||||
)
|
||||
.documentation("The camera shake to apply to the third-person camera")
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.build();
|
||||
@Nonnull
|
||||
public static final Codec<String> CHILD_ASSET_CODEC = new ContainedAssetCodec<>(CameraShake.class, CODEC);
|
||||
@Nonnull
|
||||
public static final ValidatorCache<String> VALIDATOR_CACHE = new ValidatorCache<>(new AssetKeyValidator<>(CameraShake::getAssetStore));
|
||||
private static AssetStore<String, CameraShake, IndexedAssetMap<String, CameraShake>> ASSET_STORE;
|
||||
protected String id;
|
||||
protected AssetExtraInfo.Data data;
|
||||
@Nonnull
|
||||
protected CameraShakeConfig firstPerson;
|
||||
@Nonnull
|
||||
protected CameraShakeConfig thirdPerson;
|
||||
|
||||
@Nonnull
|
||||
public static AssetStore<String, CameraShake, IndexedAssetMap<String, CameraShake>> getAssetStore() {
|
||||
if (ASSET_STORE == null) {
|
||||
ASSET_STORE = AssetRegistry.getAssetStore(CameraShake.class);
|
||||
}
|
||||
|
||||
return ASSET_STORE;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static IndexedAssetMap<String, CameraShake> getAssetMap() {
|
||||
return (IndexedAssetMap<String, CameraShake>)getAssetStore().getAssetMap();
|
||||
}
|
||||
|
||||
public CameraShake() {
|
||||
}
|
||||
|
||||
public CameraShake(@Nonnull String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public com.hypixel.hytale.protocol.CameraShake toPacket() {
|
||||
return new com.hypixel.hytale.protocol.CameraShake(this.firstPerson.toPacket(), this.thirdPerson.toPacket());
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CameraShake{id='" + this.id + "', data=" + this.data + ", firstPerson=" + this.firstPerson + ", thirdPerson=" + this.thirdPerson + "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.hypixel.hytale.builtin.adventure.camera.asset.camerashake;
|
||||
|
||||
import com.hypixel.hytale.assetstore.map.IndexedAssetMap;
|
||||
import com.hypixel.hytale.protocol.CachedPacket;
|
||||
import com.hypixel.hytale.protocol.Packet;
|
||||
import com.hypixel.hytale.protocol.UpdateType;
|
||||
import com.hypixel.hytale.protocol.packets.assets.UpdateCameraShake;
|
||||
import com.hypixel.hytale.server.core.asset.packet.SimpleAssetPacketGenerator;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class CameraShakePacketGenerator extends SimpleAssetPacketGenerator<String, CameraShake, IndexedAssetMap<String, CameraShake>> {
|
||||
public CameraShakePacketGenerator() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Packet generateInitPacket(@Nonnull IndexedAssetMap<String, CameraShake> assetMap, @Nonnull Map<String, CameraShake> assets) {
|
||||
return toCachedPacket(UpdateType.Init, assetMap, assets);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected Packet generateUpdatePacket(@Nonnull IndexedAssetMap<String, CameraShake> assetMap, @Nonnull Map<String, CameraShake> loadedAssets) {
|
||||
return toCachedPacket(UpdateType.AddOrUpdate, assetMap, loadedAssets);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected Packet generateRemovePacket(@Nonnull IndexedAssetMap<String, CameraShake> assetMap, @Nonnull Set<String> removed) {
|
||||
Int2ObjectOpenHashMap<com.hypixel.hytale.protocol.CameraShake> profiles = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
for (String key : removed) {
|
||||
int index = assetMap.getIndex(key);
|
||||
profiles.put(index, null);
|
||||
}
|
||||
|
||||
UpdateCameraShake packet = new UpdateCameraShake();
|
||||
packet.type = UpdateType.Remove;
|
||||
packet.profiles = profiles;
|
||||
return CachedPacket.cache(packet);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected static Packet toCachedPacket(UpdateType type, @Nonnull IndexedAssetMap<String, CameraShake> assetMap, @Nonnull Map<String, CameraShake> assets) {
|
||||
Int2ObjectOpenHashMap<com.hypixel.hytale.protocol.CameraShake> profiles = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
for (Entry<String, CameraShake> entry : assets.entrySet()) {
|
||||
int index = assetMap.getIndex(entry.getKey());
|
||||
profiles.put(index, entry.getValue().toPacket());
|
||||
}
|
||||
|
||||
UpdateCameraShake packet = new UpdateCameraShake();
|
||||
packet.type = type;
|
||||
packet.profiles = profiles;
|
||||
return CachedPacket.cache(packet);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.hypixel.hytale.builtin.adventure.camera.asset.viewbobbing;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetExtraInfo;
|
||||
import com.hypixel.hytale.assetstore.AssetMap;
|
||||
import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec;
|
||||
import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;
|
||||
import com.hypixel.hytale.builtin.adventure.camera.asset.CameraShakeConfig;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.codecs.EnumCodec;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.protocol.MovementType;
|
||||
import com.hypixel.hytale.server.core.io.NetworkSerializable;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class ViewBobbing
|
||||
implements NetworkSerializable<com.hypixel.hytale.protocol.ViewBobbing>,
|
||||
JsonAssetWithMap<MovementType, AssetMap<MovementType, ViewBobbing>> {
|
||||
@Nonnull
|
||||
public static final Codec<MovementType> MOVEMENT_TYPE_CODEC = new EnumCodec<>(MovementType.class);
|
||||
@Nonnull
|
||||
public static final AssetBuilderCodec<MovementType, ViewBobbing> CODEC = AssetBuilderCodec.builder(
|
||||
ViewBobbing.class, ViewBobbing::new, MOVEMENT_TYPE_CODEC, (o, v) -> o.id = v, ViewBobbing::getId, (o, data) -> o.data = data, o -> o.data
|
||||
)
|
||||
.appendInherited(
|
||||
new KeyedCodec<>("FirstPerson", CameraShakeConfig.CODEC), (o, v) -> o.firstPerson = v, o -> o.firstPerson, (o, p) -> o.firstPerson = p.firstPerson
|
||||
)
|
||||
.documentation("The camera shake profile to be applied")
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.build();
|
||||
protected MovementType id;
|
||||
protected AssetExtraInfo.Data data;
|
||||
@Nonnull
|
||||
protected CameraShakeConfig firstPerson;
|
||||
|
||||
public ViewBobbing() {
|
||||
}
|
||||
|
||||
public MovementType getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public com.hypixel.hytale.protocol.ViewBobbing toPacket() {
|
||||
return new com.hypixel.hytale.protocol.ViewBobbing(this.firstPerson.toPacket());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ViewBobbing{id=" + this.id + ", data=" + this.data + ", firstPerson=" + this.firstPerson + "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.hypixel.hytale.builtin.adventure.camera.asset.viewbobbing;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetMap;
|
||||
import com.hypixel.hytale.protocol.CachedPacket;
|
||||
import com.hypixel.hytale.protocol.MovementType;
|
||||
import com.hypixel.hytale.protocol.Packet;
|
||||
import com.hypixel.hytale.protocol.UpdateType;
|
||||
import com.hypixel.hytale.protocol.packets.assets.UpdateViewBobbing;
|
||||
import com.hypixel.hytale.server.core.asset.packet.SimpleAssetPacketGenerator;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class ViewBobbingPacketGenerator extends SimpleAssetPacketGenerator<MovementType, ViewBobbing, AssetMap<MovementType, ViewBobbing>> {
|
||||
public ViewBobbingPacketGenerator() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Packet generateInitPacket(AssetMap<MovementType, ViewBobbing> assetMap, @Nonnull Map<MovementType, ViewBobbing> assets) {
|
||||
return toCachedPacket(UpdateType.Init, assets);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
protected Packet generateUpdatePacket(AssetMap<MovementType, ViewBobbing> assetMap, @Nonnull Map<MovementType, ViewBobbing> loadedAssets) {
|
||||
return toCachedPacket(UpdateType.AddOrUpdate, loadedAssets);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
protected Packet generateRemovePacket(AssetMap<MovementType, ViewBobbing> assetMap, @Nonnull Set<MovementType> removed) {
|
||||
UpdateViewBobbing packet = new UpdateViewBobbing();
|
||||
packet.type = UpdateType.Remove;
|
||||
packet.profiles = new EnumMap<>(MovementType.class);
|
||||
|
||||
for (MovementType type : removed) {
|
||||
packet.profiles.put(type, null);
|
||||
}
|
||||
|
||||
return CachedPacket.cache(packet);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
protected static Packet toCachedPacket(UpdateType type, @Nonnull Map<MovementType, ViewBobbing> assets) {
|
||||
UpdateViewBobbing packet = new UpdateViewBobbing();
|
||||
packet.type = type;
|
||||
packet.profiles = new EnumMap<>(MovementType.class);
|
||||
|
||||
for (Entry<MovementType, ViewBobbing> entry : assets.entrySet()) {
|
||||
packet.profiles.put(entry.getKey(), entry.getValue().toPacket());
|
||||
}
|
||||
|
||||
return CachedPacket.cache(packet);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.hypixel.hytale.builtin.adventure.camera.command;
|
||||
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.server.core.Message;
|
||||
import com.hypixel.hytale.server.core.asset.type.camera.CameraEffect;
|
||||
import com.hypixel.hytale.server.core.command.system.CommandContext;
|
||||
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
|
||||
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
|
||||
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
|
||||
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgumentType;
|
||||
import com.hypixel.hytale.server.core.command.system.arguments.types.AssetArgumentType;
|
||||
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection;
|
||||
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractTargetPlayerCommand;
|
||||
import com.hypixel.hytale.server.core.modules.entity.damage.Damage;
|
||||
import com.hypixel.hytale.server.core.modules.entity.damage.DamageCause;
|
||||
import com.hypixel.hytale.server.core.modules.entity.damage.DamageSystems;
|
||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class CameraEffectCommand extends AbstractCommandCollection {
|
||||
@Nonnull
|
||||
protected static final ArgumentType<CameraEffect> CAMERA_EFFECT_ARGUMENT_TYPE = new AssetArgumentType("CameraEffect", CameraEffect.class, "");
|
||||
|
||||
public CameraEffectCommand() {
|
||||
super("camshake", "server.commands.camshake.desc");
|
||||
this.addSubCommand(new CameraEffectCommand.DamageCommand());
|
||||
this.addSubCommand(new CameraEffectCommand.DebugCommand());
|
||||
}
|
||||
|
||||
protected static class DamageCommand extends AbstractTargetPlayerCommand {
|
||||
@Nonnull
|
||||
protected static final ArgumentType<DamageCause> DAMAGE_CAUSE_ARGUMENT_TYPE = new AssetArgumentType("DamageCause", DamageCause.class, "");
|
||||
@Nonnull
|
||||
protected final OptionalArg<CameraEffect> effectArg = this.withOptionalArg(
|
||||
"effect", "server.commands.camshake.effect.desc", CameraEffectCommand.CAMERA_EFFECT_ARGUMENT_TYPE
|
||||
);
|
||||
@Nonnull
|
||||
protected final RequiredArg<DamageCause> causeArg = this.withRequiredArg(
|
||||
"cause", "server.commands.camshake.damage.cause.desc", DAMAGE_CAUSE_ARGUMENT_TYPE
|
||||
);
|
||||
@Nonnull
|
||||
protected final RequiredArg<Float> damageArg = this.withRequiredArg("amount", "server.commands.camshake.damage.amount.desc", ArgTypes.FLOAT);
|
||||
|
||||
public DamageCommand() {
|
||||
super("damage", "server.commands.camshake.damage.desc");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(
|
||||
@Nonnull CommandContext context,
|
||||
@Nullable Ref<EntityStore> sourceRef,
|
||||
@Nonnull Ref<EntityStore> ref,
|
||||
@Nonnull PlayerRef playerRef,
|
||||
@Nonnull World world,
|
||||
@Nonnull Store<EntityStore> store
|
||||
) {
|
||||
DamageCause damageCause = context.get(this.causeArg);
|
||||
float damageAmount = context.get(this.damageArg);
|
||||
Damage.CommandSource damageSource = new Damage.CommandSource(context.sender(), this.getName());
|
||||
Damage damageEvent = new Damage(damageSource, damageCause, damageAmount);
|
||||
String cameraEffectId = "Default";
|
||||
if (this.effectArg.provided(context)) {
|
||||
cameraEffectId = context.get(this.effectArg).getId();
|
||||
Damage.CameraEffect damageEffect = new Damage.CameraEffect(CameraEffect.getAssetMap().getIndex(cameraEffectId));
|
||||
damageEvent.getMetaStore().putMetaObject(Damage.CAMERA_EFFECT, damageEffect);
|
||||
}
|
||||
|
||||
DamageSystems.executeDamage(ref, store, damageEvent);
|
||||
context.sendMessage(
|
||||
Message.translation("server.commands.camshake.damage.success")
|
||||
.param("effect", cameraEffectId)
|
||||
.param("cause", damageCause.getId())
|
||||
.param("amount", damageAmount)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected static class DebugCommand extends AbstractTargetPlayerCommand {
|
||||
private static final String MESSAGE_SUCCESS = "server.commands.camshake.debug.success";
|
||||
@Nonnull
|
||||
protected final RequiredArg<CameraEffect> effectArg = this.withRequiredArg(
|
||||
"effect", "server.commands.camshake.effect.desc", CameraEffectCommand.CAMERA_EFFECT_ARGUMENT_TYPE
|
||||
);
|
||||
@Nonnull
|
||||
protected final RequiredArg<Float> intensityArg = this.withRequiredArg("intensity", "server.commands.camshake.debug.intensity.desc", ArgTypes.FLOAT);
|
||||
|
||||
public DebugCommand() {
|
||||
super("debug", "server.commands.camshake.debug.desc");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(
|
||||
@Nonnull CommandContext context,
|
||||
@Nullable Ref<EntityStore> sourceRef,
|
||||
@Nonnull Ref<EntityStore> ref,
|
||||
@Nonnull PlayerRef playerRef,
|
||||
@Nonnull World world,
|
||||
@Nonnull Store<EntityStore> store
|
||||
) {
|
||||
CameraEffect cameraEffect = context.get(this.effectArg);
|
||||
float intensity = context.get(this.intensityArg);
|
||||
PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType());
|
||||
|
||||
assert playerRefComponent != null;
|
||||
|
||||
playerRefComponent.getPacketHandler().writeNoCache(cameraEffect.createCameraShakePacket(intensity));
|
||||
context.sendMessage(Message.translation("server.commands.camshake.debug.success").param("effect", cameraEffect.getId()).param("intensity", intensity));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.hypixel.hytale.builtin.adventure.camera.interaction;
|
||||
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.protocol.InteractionType;
|
||||
import com.hypixel.hytale.server.core.asset.type.camera.CameraEffect;
|
||||
import com.hypixel.hytale.server.core.entity.InteractionContext;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction;
|
||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class CameraShakeInteraction extends SimpleInstantInteraction {
|
||||
@Nonnull
|
||||
public static final BuilderCodec<CameraShakeInteraction> CODEC = BuilderCodec.builder(
|
||||
CameraShakeInteraction.class, CameraShakeInteraction::new, SimpleInstantInteraction.CODEC
|
||||
)
|
||||
.documentation("Triggers a camera shake effect on use.")
|
||||
.<String>appendInherited(
|
||||
new KeyedCodec<>("CameraEffect", CameraEffect.CHILD_ASSET_CODEC),
|
||||
(interaction, effect) -> interaction.effectId = effect,
|
||||
interaction -> interaction.effectId,
|
||||
(interaction, parent) -> interaction.effectId = parent.effectId
|
||||
)
|
||||
.addValidator(Validators.nonNull())
|
||||
.addValidator(CameraEffect.VALIDATOR_CACHE.getValidator())
|
||||
.add()
|
||||
.afterDecode(cameraShakeInteraction -> {
|
||||
if (cameraShakeInteraction.effectId != null) {
|
||||
cameraShakeInteraction.effectIndex = CameraEffect.getAssetMap().getIndex(cameraShakeInteraction.effectId);
|
||||
}
|
||||
})
|
||||
.build();
|
||||
@Nullable
|
||||
protected String effectId;
|
||||
protected int effectIndex = Integer.MIN_VALUE;
|
||||
|
||||
public CameraShakeInteraction() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void firstRun(@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nonnull CooldownHandler cooldownHandler) {
|
||||
if (this.effectIndex != Integer.MIN_VALUE) {
|
||||
Ref<EntityStore> ref = context.getEntity();
|
||||
CommandBuffer<EntityStore> commandBuffer = context.getCommandBuffer();
|
||||
|
||||
assert commandBuffer != null;
|
||||
|
||||
PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType());
|
||||
if (playerRefComponent != null) {
|
||||
CameraEffect cameraShakeEffect = CameraEffect.getAssetMap().getAsset(this.effectIndex);
|
||||
if (cameraShakeEffect != null) {
|
||||
playerRefComponent.getPacketHandler().writeNoCache(cameraShakeEffect.createCameraShakePacket());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CameraShakeInteraction{effectId='" + this.effectId + "', effectIndex=" + this.effectIndex + "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.hypixel.hytale.builtin.adventure.camera.system;
|
||||
|
||||
import com.hypixel.hytale.component.ArchetypeChunk;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.ComponentType;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.component.SystemGroup;
|
||||
import com.hypixel.hytale.component.query.Query;
|
||||
import com.hypixel.hytale.math.util.MathUtil;
|
||||
import com.hypixel.hytale.server.core.asset.type.camera.CameraEffect;
|
||||
import com.hypixel.hytale.server.core.asset.type.gameplay.CameraEffectsConfig;
|
||||
import com.hypixel.hytale.server.core.modules.entity.damage.Damage;
|
||||
import com.hypixel.hytale.server.core.modules.entity.damage.DamageEventSystem;
|
||||
import com.hypixel.hytale.server.core.modules.entity.damage.DamageModule;
|
||||
import com.hypixel.hytale.server.core.modules.entitystats.EntityStatMap;
|
||||
import com.hypixel.hytale.server.core.modules.entitystats.EntityStatValue;
|
||||
import com.hypixel.hytale.server.core.modules.entitystats.asset.DefaultEntityStatTypes;
|
||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class CameraEffectSystem extends DamageEventSystem {
|
||||
@Nonnull
|
||||
private static final ComponentType<EntityStore, PlayerRef> PLAYER_REF_COMPONENT_TYPE = PlayerRef.getComponentType();
|
||||
private static final ComponentType<EntityStore, EntityStatMap> ENTITY_STAT_MAP_COMPONENT_TYPE = EntityStatMap.getComponentType();
|
||||
@Nonnull
|
||||
private static final Query<EntityStore> QUERY = Query.and(PLAYER_REF_COMPONENT_TYPE, ENTITY_STAT_MAP_COMPONENT_TYPE);
|
||||
|
||||
public CameraEffectSystem() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public SystemGroup<EntityStore> getGroup() {
|
||||
return DamageModule.get().getInspectDamageGroup();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Query<EntityStore> getQuery() {
|
||||
return QUERY;
|
||||
}
|
||||
|
||||
public void handle(
|
||||
int index,
|
||||
@Nonnull ArchetypeChunk<EntityStore> archetypeChunk,
|
||||
@Nonnull Store<EntityStore> store,
|
||||
@Nonnull CommandBuffer<EntityStore> commandBuffer,
|
||||
@Nonnull Damage damage
|
||||
) {
|
||||
EntityStatMap entityStatMapComponent = archetypeChunk.getComponent(index, ENTITY_STAT_MAP_COMPONENT_TYPE);
|
||||
|
||||
assert entityStatMapComponent != null;
|
||||
|
||||
EntityStatValue healthStat = entityStatMapComponent.get(DefaultEntityStatTypes.getHealth());
|
||||
if (healthStat != null) {
|
||||
float health = healthStat.getMax() - healthStat.getMin();
|
||||
if (!(health <= 0.0F)) {
|
||||
PlayerRef playerRefComponent = archetypeChunk.getComponent(index, PLAYER_REF_COMPONENT_TYPE);
|
||||
|
||||
assert playerRefComponent != null;
|
||||
|
||||
World world = commandBuffer.getExternalData().getWorld();
|
||||
CameraEffectsConfig cameraEffectsConfig = world.getGameplayConfig().getCameraEffectsConfig();
|
||||
Damage.CameraEffect effect = damage.getIfPresentMetaObject(Damage.CAMERA_EFFECT);
|
||||
int effectIndex = effect != null ? effect.getEffectIndex() : cameraEffectsConfig.getCameraEffectIndex(damage.getDamageCauseIndex());
|
||||
if (effectIndex != Integer.MIN_VALUE) {
|
||||
CameraEffect cameraEffect = CameraEffect.getAssetMap().getAsset(effectIndex);
|
||||
if (cameraEffect != null) {
|
||||
float intensity = MathUtil.clamp(damage.getAmount() / health, 0.0F, 1.0F);
|
||||
playerRefComponent.getPacketHandler().writeNoCache(cameraEffect.createCameraShakePacket(intensity));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming;
|
||||
|
||||
import com.hypixel.hytale.assetstore.map.DefaultAssetMap;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.component.CoopResidentComponent;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.config.FarmingCoopAsset;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.config.modifiers.FertilizerGrowthModifierAsset;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.config.modifiers.LightLevelGrowthModifierAsset;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.config.modifiers.WaterGrowthModifierAsset;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.config.stages.BlockStateFarmingStageData;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.config.stages.BlockTypeFarmingStageData;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.config.stages.PrefabFarmingStageData;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.config.stages.spread.DirectionalGrowthBehaviour;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.config.stages.spread.SpreadFarmingStageData;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.config.stages.spread.SpreadGrowthBehaviour;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.interactions.ChangeFarmingStageInteraction;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.interactions.FertilizeSoilInteraction;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.interactions.HarvestCropInteraction;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.interactions.UseCaptureCrateInteraction;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.interactions.UseCoopInteraction;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.interactions.UseWateringCanInteraction;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.CoopBlock;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlockState;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.TilledSoilBlock;
|
||||
import com.hypixel.hytale.builtin.tagset.config.NPCGroup;
|
||||
import com.hypixel.hytale.component.ComponentType;
|
||||
import com.hypixel.hytale.component.Holder;
|
||||
import com.hypixel.hytale.event.EventPriority;
|
||||
import com.hypixel.hytale.server.core.asset.HytaleAssetStore;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingStageData;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.GrowthModifierAsset;
|
||||
import com.hypixel.hytale.server.core.asset.type.item.config.ItemDropList;
|
||||
import com.hypixel.hytale.server.core.asset.type.weather.config.Weather;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
|
||||
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
|
||||
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.events.ChunkPreLoadProcessEvent;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class FarmingPlugin extends JavaPlugin {
|
||||
protected static FarmingPlugin instance;
|
||||
private ComponentType<ChunkStore, TilledSoilBlock> tiledSoilBlockComponentType;
|
||||
private ComponentType<ChunkStore, FarmingBlock> farmingBlockComponentType;
|
||||
private ComponentType<ChunkStore, FarmingBlockState> farmingBlockStateComponentType;
|
||||
private ComponentType<ChunkStore, CoopBlock> coopBlockStateComponentType;
|
||||
private ComponentType<EntityStore, CoopResidentComponent> coopResidentComponentType;
|
||||
|
||||
public static FarmingPlugin get() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public FarmingPlugin(@Nonnull JavaPluginInit init) {
|
||||
super(init);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setup() {
|
||||
instance = this;
|
||||
this.getAssetRegistry()
|
||||
.register(
|
||||
((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder(
|
||||
GrowthModifierAsset.class, new DefaultAssetMap()
|
||||
)
|
||||
.setPath("Farming/Modifiers"))
|
||||
.setCodec(GrowthModifierAsset.CODEC))
|
||||
.loadsAfter(Weather.class))
|
||||
.setKeyFunction(GrowthModifierAsset::getId))
|
||||
.build()
|
||||
);
|
||||
this.getAssetRegistry()
|
||||
.register(
|
||||
((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)((HytaleAssetStore.Builder)HytaleAssetStore.builder(
|
||||
FarmingCoopAsset.class, new DefaultAssetMap()
|
||||
)
|
||||
.setPath("Farming/Coops"))
|
||||
.setCodec(FarmingCoopAsset.CODEC))
|
||||
.loadsAfter(ItemDropList.class, NPCGroup.class))
|
||||
.setKeyFunction(FarmingCoopAsset::getId))
|
||||
.build()
|
||||
);
|
||||
this.getCodecRegistry(Interaction.CODEC)
|
||||
.register("HarvestCrop", HarvestCropInteraction.class, HarvestCropInteraction.CODEC)
|
||||
.register("FertilizeSoil", FertilizeSoilInteraction.class, FertilizeSoilInteraction.CODEC)
|
||||
.register("ChangeFarmingStage", ChangeFarmingStageInteraction.class, ChangeFarmingStageInteraction.CODEC)
|
||||
.register("UseWateringCan", UseWateringCanInteraction.class, UseWateringCanInteraction.CODEC)
|
||||
.register("UseCoop", UseCoopInteraction.class, UseCoopInteraction.CODEC)
|
||||
.register("UseCaptureCrate", UseCaptureCrateInteraction.class, UseCaptureCrateInteraction.CODEC);
|
||||
this.getCodecRegistry(GrowthModifierAsset.CODEC).register("Fertilizer", FertilizerGrowthModifierAsset.class, FertilizerGrowthModifierAsset.CODEC);
|
||||
this.getCodecRegistry(GrowthModifierAsset.CODEC).register("LightLevel", LightLevelGrowthModifierAsset.class, LightLevelGrowthModifierAsset.CODEC);
|
||||
this.getCodecRegistry(GrowthModifierAsset.CODEC).register("Water", WaterGrowthModifierAsset.class, WaterGrowthModifierAsset.CODEC);
|
||||
this.getCodecRegistry(FarmingStageData.CODEC).register("BlockType", BlockTypeFarmingStageData.class, BlockTypeFarmingStageData.CODEC);
|
||||
this.getCodecRegistry(FarmingStageData.CODEC).register("BlockState", BlockStateFarmingStageData.class, BlockStateFarmingStageData.CODEC);
|
||||
this.getCodecRegistry(FarmingStageData.CODEC).register("Prefab", PrefabFarmingStageData.class, PrefabFarmingStageData.CODEC);
|
||||
this.getCodecRegistry(FarmingStageData.CODEC).register("Spread", SpreadFarmingStageData.class, SpreadFarmingStageData.CODEC);
|
||||
this.getCodecRegistry(SpreadGrowthBehaviour.CODEC).register("Directional", DirectionalGrowthBehaviour.class, DirectionalGrowthBehaviour.CODEC);
|
||||
this.tiledSoilBlockComponentType = this.getChunkStoreRegistry().registerComponent(TilledSoilBlock.class, "TilledSoil", TilledSoilBlock.CODEC);
|
||||
this.farmingBlockComponentType = this.getChunkStoreRegistry().registerComponent(FarmingBlock.class, "FarmingBlock", FarmingBlock.CODEC);
|
||||
this.farmingBlockStateComponentType = this.getChunkStoreRegistry().registerComponent(FarmingBlockState.class, "Farming", FarmingBlockState.CODEC);
|
||||
this.coopBlockStateComponentType = this.getChunkStoreRegistry().registerComponent(CoopBlock.class, "Coop", CoopBlock.CODEC);
|
||||
this.coopResidentComponentType = this.getEntityStoreRegistry()
|
||||
.registerComponent(CoopResidentComponent.class, "CoopResident", CoopResidentComponent.CODEC);
|
||||
this.getChunkStoreRegistry().registerSystem(new FarmingSystems.OnSoilAdded());
|
||||
this.getChunkStoreRegistry().registerSystem(new FarmingSystems.OnFarmBlockAdded());
|
||||
this.getChunkStoreRegistry().registerSystem(new FarmingSystems.Ticking());
|
||||
this.getChunkStoreRegistry().registerSystem(new FarmingSystems.MigrateFarming());
|
||||
this.getChunkStoreRegistry().registerSystem(new FarmingSystems.OnCoopAdded());
|
||||
this.getEntityStoreRegistry().registerSystem(new FarmingSystems.CoopResidentEntitySystem());
|
||||
this.getEntityStoreRegistry().registerSystem(new FarmingSystems.CoopResidentTicking());
|
||||
this.getEventRegistry().registerGlobal(EventPriority.LAST, ChunkPreLoadProcessEvent.class, FarmingPlugin::preventSpreadOnNew);
|
||||
}
|
||||
|
||||
private static void preventSpreadOnNew(ChunkPreLoadProcessEvent event) {
|
||||
if (event.isNewlyGenerated()) {
|
||||
BlockComponentChunk components = event.getHolder().getComponent(BlockComponentChunk.getComponentType());
|
||||
if (components != null) {
|
||||
Int2ObjectMap<Holder<ChunkStore>> holders = components.getEntityHolders();
|
||||
holders.values().forEach(v -> {
|
||||
FarmingBlock farming = v.getComponent(FarmingBlock.getComponentType());
|
||||
if (farming != null) {
|
||||
farming.setSpreadRate(0.0F);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ComponentType<ChunkStore, TilledSoilBlock> getTiledSoilBlockComponentType() {
|
||||
return this.tiledSoilBlockComponentType;
|
||||
}
|
||||
|
||||
public ComponentType<ChunkStore, FarmingBlock> getFarmingBlockComponentType() {
|
||||
return this.farmingBlockComponentType;
|
||||
}
|
||||
|
||||
public ComponentType<ChunkStore, FarmingBlockState> getFarmingBlockStateComponentType() {
|
||||
return this.farmingBlockStateComponentType;
|
||||
}
|
||||
|
||||
public ComponentType<ChunkStore, CoopBlock> getCoopBlockStateComponentType() {
|
||||
return this.coopBlockStateComponentType;
|
||||
}
|
||||
|
||||
public ComponentType<EntityStore, CoopResidentComponent> getCoopResidentComponentType() {
|
||||
return this.coopResidentComponentType;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,690 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.component.CoopResidentComponent;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.config.FarmingCoopAsset;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.config.stages.BlockStateFarmingStageData;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.config.stages.BlockTypeFarmingStageData;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.CoopBlock;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlockState;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.TilledSoilBlock;
|
||||
import com.hypixel.hytale.component.AddReason;
|
||||
import com.hypixel.hytale.component.ArchetypeChunk;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.ComponentType;
|
||||
import com.hypixel.hytale.component.Holder;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.RemoveReason;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.component.query.Query;
|
||||
import com.hypixel.hytale.component.system.RefSystem;
|
||||
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
import com.hypixel.hytale.math.vector.Vector3d;
|
||||
import com.hypixel.hytale.math.vector.Vector3i;
|
||||
import com.hypixel.hytale.protocol.Rangef;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktick.BlockTickStrategy;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingData;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingStageData;
|
||||
import com.hypixel.hytale.server.core.entity.UUIDComponent;
|
||||
import com.hypixel.hytale.server.core.modules.block.BlockModule;
|
||||
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.ChunkColumn;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import com.hypixel.hytale.server.core.util.TargetUtil;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class FarmingSystems {
|
||||
public FarmingSystems() {
|
||||
}
|
||||
|
||||
private static boolean hasCropAbove(BlockChunk blockChunk, int x, int y, int z) {
|
||||
if (y + 1 >= 320) {
|
||||
return false;
|
||||
} else {
|
||||
BlockSection aboveBlockSection = blockChunk.getSectionAtBlockY(y + 1);
|
||||
if (aboveBlockSection == null) {
|
||||
return false;
|
||||
} else {
|
||||
BlockType aboveBlockType = BlockType.getAssetMap().getAsset(aboveBlockSection.get(x, y + 1, z));
|
||||
if (aboveBlockType == null) {
|
||||
return false;
|
||||
} else {
|
||||
FarmingData farmingConfig = aboveBlockType.getFarming();
|
||||
return farmingConfig != null && farmingConfig.getStages() != null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean updateSoilDecayTime(CommandBuffer<ChunkStore> commandBuffer, TilledSoilBlock soilBlock, BlockType blockType) {
|
||||
if (blockType != null && blockType.getFarming() != null && blockType.getFarming().getSoilConfig() != null) {
|
||||
FarmingData.SoilConfig soilConfig = blockType.getFarming().getSoilConfig();
|
||||
Rangef range = soilConfig.getLifetime();
|
||||
if (range == null) {
|
||||
return false;
|
||||
} else {
|
||||
double baseDuration = range.min + (range.max - range.min) * ThreadLocalRandom.current().nextDouble();
|
||||
Instant currentTime = commandBuffer.getExternalData()
|
||||
.getWorld()
|
||||
.getEntityStore()
|
||||
.getStore()
|
||||
.getResource(WorldTimeResource.getResourceType())
|
||||
.getGameTime();
|
||||
Instant endTime = currentTime.plus(Math.round(baseDuration), ChronoUnit.SECONDS);
|
||||
soilBlock.setDecayTime(endTime);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static class CoopResidentEntitySystem extends RefSystem<EntityStore> {
|
||||
private static final ComponentType<EntityStore, CoopResidentComponent> componentType = CoopResidentComponent.getComponentType();
|
||||
|
||||
public CoopResidentEntitySystem() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<EntityStore> getQuery() {
|
||||
return componentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityAdded(
|
||||
@Nonnull Ref<EntityStore> ref, @Nonnull AddReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer
|
||||
) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityRemove(
|
||||
@Nonnull Ref<EntityStore> ref, @Nonnull RemoveReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer
|
||||
) {
|
||||
if (reason != RemoveReason.UNLOAD) {
|
||||
UUIDComponent uuidComponent = commandBuffer.getComponent(ref, UUIDComponent.getComponentType());
|
||||
if (uuidComponent != null) {
|
||||
UUID uuid = uuidComponent.getUuid();
|
||||
CoopResidentComponent coopResidentComponent = commandBuffer.getComponent(ref, componentType);
|
||||
if (coopResidentComponent != null) {
|
||||
Vector3i coopPosition = coopResidentComponent.getCoopLocation();
|
||||
World world = commandBuffer.getExternalData().getWorld();
|
||||
long chunkIndex = ChunkUtil.indexChunkFromBlock(coopPosition.x, coopPosition.z);
|
||||
WorldChunk chunk = world.getChunkIfLoaded(chunkIndex);
|
||||
if (chunk != null) {
|
||||
Ref<ChunkStore> chunkReference = world.getChunkStore().getChunkReference(chunkIndex);
|
||||
if (chunkReference != null && chunkReference.isValid()) {
|
||||
Store<ChunkStore> chunkStore = world.getChunkStore().getStore();
|
||||
ChunkColumn chunkColumnComponent = chunkStore.getComponent(chunkReference, ChunkColumn.getComponentType());
|
||||
if (chunkColumnComponent != null) {
|
||||
BlockChunk blockChunkComponent = chunkStore.getComponent(chunkReference, BlockChunk.getComponentType());
|
||||
if (blockChunkComponent != null) {
|
||||
Ref<ChunkStore> sectionRef = chunkColumnComponent.getSection(ChunkUtil.chunkCoordinate(coopPosition.y));
|
||||
if (sectionRef != null && sectionRef.isValid()) {
|
||||
BlockComponentChunk blockComponentChunk = chunkStore.getComponent(chunkReference, BlockComponentChunk.getComponentType());
|
||||
if (blockComponentChunk != null) {
|
||||
int blockIndexColumn = ChunkUtil.indexBlockInColumn(coopPosition.x, coopPosition.y, coopPosition.z);
|
||||
Ref<ChunkStore> coopEntityReference = blockComponentChunk.getEntityReference(blockIndexColumn);
|
||||
if (coopEntityReference != null) {
|
||||
CoopBlock coop = chunkStore.getComponent(coopEntityReference, CoopBlock.getComponentType());
|
||||
if (coop != null) {
|
||||
coop.handleResidentDespawn(uuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CoopResidentTicking extends EntityTickingSystem<EntityStore> {
|
||||
private static final ComponentType<EntityStore, CoopResidentComponent> componentType = CoopResidentComponent.getComponentType();
|
||||
|
||||
public CoopResidentTicking() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<EntityStore> getQuery() {
|
||||
return componentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(
|
||||
float dt,
|
||||
int index,
|
||||
@Nonnull ArchetypeChunk<EntityStore> archetypeChunk,
|
||||
@Nonnull Store<EntityStore> store,
|
||||
@Nonnull CommandBuffer<EntityStore> commandBuffer
|
||||
) {
|
||||
CoopResidentComponent coopResidentComponent = archetypeChunk.getComponent(index, CoopResidentComponent.getComponentType());
|
||||
if (coopResidentComponent != null) {
|
||||
if (coopResidentComponent.getMarkedForDespawn()) {
|
||||
commandBuffer.removeEntity(archetypeChunk.getReferenceTo(index), RemoveReason.REMOVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public static class MigrateFarming extends BlockModule.MigrationSystem {
|
||||
public MigrateFarming() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityAdd(@Nonnull Holder<ChunkStore> holder, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store) {
|
||||
FarmingBlockState oldState = holder.getComponent(FarmingPlugin.get().getFarmingBlockStateComponentType());
|
||||
FarmingBlock farming = new FarmingBlock();
|
||||
farming.setGrowthProgress(oldState.getCurrentFarmingStageIndex());
|
||||
farming.setCurrentStageSet(oldState.getCurrentFarmingStageSetName());
|
||||
farming.setSpreadRate(oldState.getSpreadRate());
|
||||
holder.putComponent(FarmingBlock.getComponentType(), farming);
|
||||
holder.removeComponent(FarmingPlugin.get().getFarmingBlockStateComponentType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityRemoved(@Nonnull Holder<ChunkStore> holder, @Nonnull RemoveReason reason, @Nonnull Store<ChunkStore> store) {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Query<ChunkStore> getQuery() {
|
||||
return FarmingPlugin.get().getFarmingBlockStateComponentType();
|
||||
}
|
||||
}
|
||||
|
||||
public static class OnCoopAdded extends RefSystem<ChunkStore> {
|
||||
private static final Query<ChunkStore> QUERY = Query.and(BlockModule.BlockStateInfo.getComponentType(), CoopBlock.getComponentType());
|
||||
|
||||
public OnCoopAdded() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityAdded(
|
||||
@Nonnull Ref<ChunkStore> ref, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer
|
||||
) {
|
||||
CoopBlock coopBlock = commandBuffer.getComponent(ref, CoopBlock.getComponentType());
|
||||
if (coopBlock != null) {
|
||||
WorldTimeResource worldTimeResource = commandBuffer.getExternalData()
|
||||
.getWorld()
|
||||
.getEntityStore()
|
||||
.getStore()
|
||||
.getResource(WorldTimeResource.getResourceType());
|
||||
BlockModule.BlockStateInfo info = commandBuffer.getComponent(ref, BlockModule.BlockStateInfo.getComponentType());
|
||||
|
||||
assert info != null;
|
||||
|
||||
int x = ChunkUtil.xFromBlockInColumn(info.getIndex());
|
||||
int y = ChunkUtil.yFromBlockInColumn(info.getIndex());
|
||||
int z = ChunkUtil.zFromBlockInColumn(info.getIndex());
|
||||
BlockChunk blockChunk = commandBuffer.getComponent(info.getChunkRef(), BlockChunk.getComponentType());
|
||||
|
||||
assert blockChunk != null;
|
||||
|
||||
BlockSection blockSection = blockChunk.getSectionAtBlockY(y);
|
||||
blockSection.scheduleTick(ChunkUtil.indexBlock(x, y, z), coopBlock.getNextScheduledTick(worldTimeResource));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityRemove(
|
||||
@Nonnull Ref<ChunkStore> ref, @Nonnull RemoveReason reason, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer
|
||||
) {
|
||||
if (reason != RemoveReason.UNLOAD) {
|
||||
CoopBlock coop = commandBuffer.getComponent(ref, CoopBlock.getComponentType());
|
||||
if (coop != null) {
|
||||
BlockModule.BlockStateInfo info = commandBuffer.getComponent(ref, BlockModule.BlockStateInfo.getComponentType());
|
||||
|
||||
assert info != null;
|
||||
|
||||
Store<EntityStore> entityStore = commandBuffer.getExternalData().getWorld().getEntityStore().getStore();
|
||||
int x = ChunkUtil.xFromBlockInColumn(info.getIndex());
|
||||
int y = ChunkUtil.yFromBlockInColumn(info.getIndex());
|
||||
int z = ChunkUtil.zFromBlockInColumn(info.getIndex());
|
||||
BlockChunk blockChunk = commandBuffer.getComponent(info.getChunkRef(), BlockChunk.getComponentType());
|
||||
|
||||
assert blockChunk != null;
|
||||
|
||||
ChunkColumn column = commandBuffer.getComponent(info.getChunkRef(), ChunkColumn.getComponentType());
|
||||
|
||||
assert column != null;
|
||||
|
||||
Ref<ChunkStore> sectionRef = column.getSection(ChunkUtil.chunkCoordinate(y));
|
||||
|
||||
assert sectionRef != null;
|
||||
|
||||
BlockSection blockSection = commandBuffer.getComponent(sectionRef, BlockSection.getComponentType());
|
||||
|
||||
assert blockSection != null;
|
||||
|
||||
ChunkSection chunkSection = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
|
||||
|
||||
assert chunkSection != null;
|
||||
|
||||
int worldX = ChunkUtil.worldCoordFromLocalCoord(chunkSection.getX(), x);
|
||||
int worldY = ChunkUtil.worldCoordFromLocalCoord(chunkSection.getY(), y);
|
||||
int worldZ = ChunkUtil.worldCoordFromLocalCoord(chunkSection.getZ(), z);
|
||||
World world = commandBuffer.getExternalData().getWorld();
|
||||
WorldTimeResource worldTimeResource = world.getEntityStore().getStore().getResource(WorldTimeResource.getResourceType());
|
||||
coop.handleBlockBroken(world, worldTimeResource, entityStore, worldX, worldY, worldZ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Query<ChunkStore> getQuery() {
|
||||
return QUERY;
|
||||
}
|
||||
}
|
||||
|
||||
public static class OnFarmBlockAdded extends RefSystem<ChunkStore> {
|
||||
private static final Query<ChunkStore> QUERY = Query.and(BlockModule.BlockStateInfo.getComponentType(), FarmingBlock.getComponentType());
|
||||
|
||||
public OnFarmBlockAdded() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityAdded(
|
||||
@Nonnull Ref<ChunkStore> ref, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer
|
||||
) {
|
||||
FarmingBlock farmingBlock = commandBuffer.getComponent(ref, FarmingBlock.getComponentType());
|
||||
|
||||
assert farmingBlock != null;
|
||||
|
||||
BlockModule.BlockStateInfo info = commandBuffer.getComponent(ref, BlockModule.BlockStateInfo.getComponentType());
|
||||
|
||||
assert info != null;
|
||||
|
||||
BlockChunk blockChunk = commandBuffer.getComponent(info.getChunkRef(), BlockChunk.getComponentType());
|
||||
if (farmingBlock.getLastTickGameTime() == null) {
|
||||
int blockId = blockChunk.getBlock(
|
||||
ChunkUtil.xFromBlockInColumn(info.getIndex()), ChunkUtil.yFromBlockInColumn(info.getIndex()), ChunkUtil.zFromBlockInColumn(info.getIndex())
|
||||
);
|
||||
BlockType blockType = BlockType.getAssetMap().getAsset(blockId);
|
||||
if (blockType.getFarming() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
farmingBlock.setCurrentStageSet(blockType.getFarming().getStartingStageSet());
|
||||
farmingBlock.setLastTickGameTime(
|
||||
store.getExternalData().getWorld().getEntityStore().getStore().getResource(WorldTimeResource.getResourceType()).getGameTime()
|
||||
);
|
||||
blockChunk.markNeedsSaving();
|
||||
if (blockType.getFarming().getStages() != null) {
|
||||
FarmingStageData[] stages = blockType.getFarming().getStages().get(blockType.getFarming().getStartingStageSet());
|
||||
if (stages != null && stages.length > 0) {
|
||||
boolean found = false;
|
||||
|
||||
for (int i = 0; i < stages.length; i++) {
|
||||
FarmingStageData stage = stages[i];
|
||||
switch (stage) {
|
||||
case BlockTypeFarmingStageData data:
|
||||
if (data.getBlock().equals(blockType.getId())) {
|
||||
farmingBlock.setGrowthProgress(i);
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
case BlockStateFarmingStageData datax:
|
||||
BlockType stateBlockType = blockType.getBlockForState(datax.getState());
|
||||
if (stateBlockType != null && stateBlockType.getId().equals(blockType.getId())) {
|
||||
farmingBlock.setGrowthProgress(i);
|
||||
found = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
Ref<ChunkStore> sectionRef = commandBuffer.getComponent(info.getChunkRef(), ChunkColumn.getComponentType())
|
||||
.getSection(ChunkUtil.chunkCoordinate(ChunkUtil.yFromBlockInColumn(info.getIndex())));
|
||||
stages[0]
|
||||
.apply(
|
||||
commandBuffer,
|
||||
sectionRef,
|
||||
ref,
|
||||
ChunkUtil.xFromBlockInColumn(info.getIndex()),
|
||||
ChunkUtil.yFromBlockInColumn(info.getIndex()),
|
||||
ChunkUtil.zFromBlockInColumn(info.getIndex()),
|
||||
null
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (farmingBlock.getLastTickGameTime() == null) {
|
||||
farmingBlock.setLastTickGameTime(
|
||||
store.getExternalData().getWorld().getEntityStore().getStore().getResource(WorldTimeResource.getResourceType()).getGameTime()
|
||||
);
|
||||
blockChunk.markNeedsSaving();
|
||||
}
|
||||
|
||||
int x = ChunkUtil.xFromBlockInColumn(info.getIndex());
|
||||
int y = ChunkUtil.yFromBlockInColumn(info.getIndex());
|
||||
int z = ChunkUtil.zFromBlockInColumn(info.getIndex());
|
||||
BlockComponentChunk blockComponentChunk = commandBuffer.getComponent(info.getChunkRef(), BlockComponentChunk.getComponentType());
|
||||
|
||||
assert blockComponentChunk != null;
|
||||
|
||||
ChunkColumn column = commandBuffer.getComponent(info.getChunkRef(), ChunkColumn.getComponentType());
|
||||
|
||||
assert column != null;
|
||||
|
||||
Ref<ChunkStore> section = column.getSection(ChunkUtil.chunkCoordinate(y));
|
||||
BlockSection blockSection = commandBuffer.getComponent(section, BlockSection.getComponentType());
|
||||
FarmingUtil.tickFarming(commandBuffer, blockChunk, blockSection, section, ref, farmingBlock, x, y, z, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityRemove(
|
||||
@Nonnull Ref<ChunkStore> ref, @Nonnull RemoveReason reason, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer
|
||||
) {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Query<ChunkStore> getQuery() {
|
||||
return QUERY;
|
||||
}
|
||||
}
|
||||
|
||||
public static class OnSoilAdded extends RefSystem<ChunkStore> {
|
||||
private static final Query<ChunkStore> QUERY = Query.and(BlockModule.BlockStateInfo.getComponentType(), TilledSoilBlock.getComponentType());
|
||||
|
||||
public OnSoilAdded() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityAdded(
|
||||
@Nonnull Ref<ChunkStore> ref, @Nonnull AddReason reason, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer
|
||||
) {
|
||||
TilledSoilBlock soil = commandBuffer.getComponent(ref, TilledSoilBlock.getComponentType());
|
||||
|
||||
assert soil != null;
|
||||
|
||||
BlockModule.BlockStateInfo info = commandBuffer.getComponent(ref, BlockModule.BlockStateInfo.getComponentType());
|
||||
|
||||
assert info != null;
|
||||
|
||||
if (!soil.isPlanted()) {
|
||||
int x = ChunkUtil.xFromBlockInColumn(info.getIndex());
|
||||
int y = ChunkUtil.yFromBlockInColumn(info.getIndex());
|
||||
int z = ChunkUtil.zFromBlockInColumn(info.getIndex());
|
||||
|
||||
assert info.getChunkRef() != null;
|
||||
|
||||
BlockChunk blockChunk = commandBuffer.getComponent(info.getChunkRef(), BlockChunk.getComponentType());
|
||||
|
||||
assert blockChunk != null;
|
||||
|
||||
BlockSection blockSection = blockChunk.getSectionAtBlockY(y);
|
||||
Instant decayTime = soil.getDecayTime();
|
||||
if (decayTime == null) {
|
||||
BlockType blockType = BlockType.getAssetMap().getAsset(blockSection.get(x, y, z));
|
||||
FarmingSystems.updateSoilDecayTime(commandBuffer, soil, blockType);
|
||||
}
|
||||
|
||||
if (decayTime == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
blockSection.scheduleTick(ChunkUtil.indexBlock(x, y, z), decayTime);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityRemove(
|
||||
@Nonnull Ref<ChunkStore> ref, @Nonnull RemoveReason reason, @Nonnull Store<ChunkStore> store, @Nonnull CommandBuffer<ChunkStore> commandBuffer
|
||||
) {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Query<ChunkStore> getQuery() {
|
||||
return QUERY;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Ticking extends EntityTickingSystem<ChunkStore> {
|
||||
private static final Query<ChunkStore> QUERY = Query.and(BlockSection.getComponentType(), ChunkSection.getComponentType());
|
||||
|
||||
public Ticking() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(
|
||||
float dt,
|
||||
int index,
|
||||
@Nonnull ArchetypeChunk<ChunkStore> archetypeChunk,
|
||||
@Nonnull Store<ChunkStore> store,
|
||||
@Nonnull CommandBuffer<ChunkStore> commandBuffer
|
||||
) {
|
||||
BlockSection blocks = archetypeChunk.getComponent(index, BlockSection.getComponentType());
|
||||
|
||||
assert blocks != null;
|
||||
|
||||
if (blocks.getTickingBlocksCountCopy() != 0) {
|
||||
ChunkSection section = archetypeChunk.getComponent(index, ChunkSection.getComponentType());
|
||||
|
||||
assert section != null;
|
||||
|
||||
if (section.getChunkColumnReference() != null && section.getChunkColumnReference().isValid()) {
|
||||
BlockComponentChunk blockComponentChunk = commandBuffer.getComponent(section.getChunkColumnReference(), BlockComponentChunk.getComponentType());
|
||||
|
||||
assert blockComponentChunk != null;
|
||||
|
||||
Ref<ChunkStore> ref = archetypeChunk.getReferenceTo(index);
|
||||
BlockChunk blockChunk = commandBuffer.getComponent(section.getChunkColumnReference(), BlockChunk.getComponentType());
|
||||
|
||||
assert blockChunk != null;
|
||||
|
||||
blocks.forEachTicking(
|
||||
blockComponentChunk, commandBuffer, section.getY(), (blockComponentChunk1, commandBuffer1, localX, localY, localZ, blockId) -> {
|
||||
Ref<ChunkStore> blockRef = blockComponentChunk1.getEntityReference(ChunkUtil.indexBlockInColumn(localX, localY, localZ));
|
||||
if (blockRef == null) {
|
||||
return BlockTickStrategy.IGNORED;
|
||||
} else {
|
||||
FarmingBlock farming = commandBuffer1.getComponent(blockRef, FarmingBlock.getComponentType());
|
||||
if (farming != null) {
|
||||
FarmingUtil.tickFarming(commandBuffer1, blockChunk, blocks, ref, blockRef, farming, localX, localY, localZ, false);
|
||||
return BlockTickStrategy.SLEEP;
|
||||
} else {
|
||||
TilledSoilBlock soil = commandBuffer1.getComponent(blockRef, TilledSoilBlock.getComponentType());
|
||||
if (soil != null) {
|
||||
tickSoil(commandBuffer1, blockComponentChunk1, blockRef, soil);
|
||||
return BlockTickStrategy.SLEEP;
|
||||
} else {
|
||||
CoopBlock coop = commandBuffer1.getComponent(blockRef, CoopBlock.getComponentType());
|
||||
if (coop != null) {
|
||||
tickCoop(commandBuffer1, blockComponentChunk1, blockRef, coop);
|
||||
return BlockTickStrategy.SLEEP;
|
||||
} else {
|
||||
return BlockTickStrategy.IGNORED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void tickSoil(
|
||||
CommandBuffer<ChunkStore> commandBuffer, BlockComponentChunk blockComponentChunk, Ref<ChunkStore> blockRef, TilledSoilBlock soilBlock
|
||||
) {
|
||||
BlockModule.BlockStateInfo info = commandBuffer.getComponent(blockRef, BlockModule.BlockStateInfo.getComponentType());
|
||||
|
||||
assert info != null;
|
||||
|
||||
int x = ChunkUtil.xFromBlockInColumn(info.getIndex());
|
||||
int y = ChunkUtil.yFromBlockInColumn(info.getIndex());
|
||||
int z = ChunkUtil.zFromBlockInColumn(info.getIndex());
|
||||
if (y < 320) {
|
||||
assert info.getChunkRef() != null;
|
||||
|
||||
BlockChunk blockChunk = commandBuffer.getComponent(info.getChunkRef(), BlockChunk.getComponentType());
|
||||
|
||||
assert blockChunk != null;
|
||||
|
||||
BlockSection blockSection = blockChunk.getSectionAtBlockY(y);
|
||||
boolean hasCrop = FarmingSystems.hasCropAbove(blockChunk, x, y, z);
|
||||
BlockType blockType = BlockType.getAssetMap().getAsset(blockSection.get(x, y, z));
|
||||
Instant currentTime = commandBuffer.getExternalData()
|
||||
.getWorld()
|
||||
.getEntityStore()
|
||||
.getStore()
|
||||
.getResource(WorldTimeResource.getResourceType())
|
||||
.getGameTime();
|
||||
Instant decayTime = soilBlock.getDecayTime();
|
||||
if (soilBlock.isPlanted() && !hasCrop) {
|
||||
if (!FarmingSystems.updateSoilDecayTime(commandBuffer, soilBlock, blockType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (decayTime != null) {
|
||||
blockSection.scheduleTick(ChunkUtil.indexBlock(x, y, z), decayTime);
|
||||
}
|
||||
} else if (!soilBlock.isPlanted() && !hasCrop) {
|
||||
if (decayTime == null || !decayTime.isAfter(currentTime)) {
|
||||
assert info.getChunkRef() != null;
|
||||
|
||||
if (blockType != null && blockType.getFarming() != null && blockType.getFarming().getSoilConfig() != null) {
|
||||
FarmingData.SoilConfig soilConfig = blockType.getFarming().getSoilConfig();
|
||||
String targetBlock = soilConfig.getTargetBlock();
|
||||
if (targetBlock == null) {
|
||||
return;
|
||||
} else {
|
||||
int targetBlockId = BlockType.getAssetMap().getIndex(targetBlock);
|
||||
if (targetBlockId == Integer.MIN_VALUE) {
|
||||
return;
|
||||
} else {
|
||||
BlockType targetBlockType = BlockType.getAssetMap().getAsset(targetBlockId);
|
||||
int rotation = blockSection.getRotationIndex(x, y, z);
|
||||
WorldChunk worldChunk = commandBuffer.getComponent(info.getChunkRef(), WorldChunk.getComponentType());
|
||||
commandBuffer.run(_store -> worldChunk.setBlock(x, y, z, targetBlockId, targetBlockType, rotation, 0, 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (hasCrop) {
|
||||
soilBlock.setDecayTime(null);
|
||||
}
|
||||
|
||||
String targetBlock = soilBlock.computeBlockType(currentTime, blockType);
|
||||
if (targetBlock != null && !targetBlock.equals(blockType.getId())) {
|
||||
WorldChunk worldChunk = commandBuffer.getComponent(info.getChunkRef(), WorldChunk.getComponentType());
|
||||
int rotation = blockSection.getRotationIndex(x, y, z);
|
||||
int targetBlockId = BlockType.getAssetMap().getIndex(targetBlock);
|
||||
BlockType targetBlockType = BlockType.getAssetMap().getAsset(targetBlockId);
|
||||
commandBuffer.run(_store -> worldChunk.setBlock(x, y, z, targetBlockId, targetBlockType, rotation, 0, 2));
|
||||
}
|
||||
|
||||
soilBlock.setPlanted(hasCrop);
|
||||
}
|
||||
}
|
||||
|
||||
private static void tickCoop(
|
||||
CommandBuffer<ChunkStore> commandBuffer, BlockComponentChunk blockComponentChunk, Ref<ChunkStore> blockRef, CoopBlock coopBlock
|
||||
) {
|
||||
BlockModule.BlockStateInfo info = commandBuffer.getComponent(blockRef, BlockModule.BlockStateInfo.getComponentType());
|
||||
|
||||
assert info != null;
|
||||
|
||||
Store<EntityStore> store = commandBuffer.getExternalData().getWorld().getEntityStore().getStore();
|
||||
WorldTimeResource worldTimeResource = store.getResource(WorldTimeResource.getResourceType());
|
||||
FarmingCoopAsset coopAsset = coopBlock.getCoopAsset();
|
||||
if (coopAsset != null) {
|
||||
int x = ChunkUtil.xFromBlockInColumn(info.getIndex());
|
||||
int y = ChunkUtil.yFromBlockInColumn(info.getIndex());
|
||||
int z = ChunkUtil.zFromBlockInColumn(info.getIndex());
|
||||
BlockChunk blockChunk = commandBuffer.getComponent(info.getChunkRef(), BlockChunk.getComponentType());
|
||||
|
||||
assert blockChunk != null;
|
||||
|
||||
ChunkColumn column = commandBuffer.getComponent(info.getChunkRef(), ChunkColumn.getComponentType());
|
||||
|
||||
assert column != null;
|
||||
|
||||
Ref<ChunkStore> sectionRef = column.getSection(ChunkUtil.chunkCoordinate(y));
|
||||
|
||||
assert sectionRef != null;
|
||||
|
||||
BlockSection blockSection = commandBuffer.getComponent(sectionRef, BlockSection.getComponentType());
|
||||
|
||||
assert blockSection != null;
|
||||
|
||||
ChunkSection chunkSection = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
|
||||
|
||||
assert chunkSection != null;
|
||||
|
||||
int worldX = ChunkUtil.worldCoordFromLocalCoord(chunkSection.getX(), x);
|
||||
int worldY = ChunkUtil.worldCoordFromLocalCoord(chunkSection.getY(), y);
|
||||
int worldZ = ChunkUtil.worldCoordFromLocalCoord(chunkSection.getZ(), z);
|
||||
World world = commandBuffer.getExternalData().getWorld();
|
||||
WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(worldX, worldZ));
|
||||
double blockRotation = chunk.getRotation(worldX, worldY, worldZ).yaw().getRadians();
|
||||
Vector3d spawnOffset = new Vector3d().assign(coopAsset.getResidentSpawnOffset()).rotateY((float)blockRotation);
|
||||
Vector3i coopLocation = new Vector3i(worldX, worldY, worldZ);
|
||||
boolean tryCapture = coopAsset.getCaptureWildNPCsInRange();
|
||||
float captureRange = coopAsset.getWildCaptureRadius();
|
||||
if (tryCapture && captureRange >= 0.0F) {
|
||||
world.execute(() -> {
|
||||
for (Ref<EntityStore> entity : TargetUtil.getAllEntitiesInSphere(coopLocation.toVector3d(), captureRange, store)) {
|
||||
coopBlock.tryPutWildResidentFromWild(store, entity, worldTimeResource, coopLocation);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (coopBlock.shouldResidentsBeInCoop(worldTimeResource)) {
|
||||
world.execute(() -> coopBlock.ensureNoResidentsInWorld(store));
|
||||
} else {
|
||||
world.execute(() -> {
|
||||
coopBlock.ensureSpawnResidentsInWorld(world, store, coopLocation.toVector3d(), spawnOffset);
|
||||
coopBlock.generateProduceToInventory(worldTimeResource);
|
||||
Vector3i blockPos = new Vector3i(worldX, worldY, worldZ);
|
||||
BlockType currentBlockType = world.getBlockType(blockPos);
|
||||
|
||||
assert currentBlockType != null;
|
||||
|
||||
chunk.setBlockInteractionState(blockPos, currentBlockType, coopBlock.hasProduce() ? "Produce_Ready" : "default");
|
||||
});
|
||||
}
|
||||
|
||||
Instant nextTickInstant = coopBlock.getNextScheduledTick(worldTimeResource);
|
||||
if (nextTickInstant != null) {
|
||||
blockSection.scheduleTick(ChunkUtil.indexBlock(x, y, z), nextTickInstant);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Query<ChunkStore> getQuery() {
|
||||
return QUERY;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock;
|
||||
import com.hypixel.hytale.component.AddReason;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.ComponentAccessor;
|
||||
import com.hypixel.hytale.component.Holder;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.RemoveReason;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
import com.hypixel.hytale.math.util.HashUtil;
|
||||
import com.hypixel.hytale.math.vector.Vector3d;
|
||||
import com.hypixel.hytale.math.vector.Vector3i;
|
||||
import com.hypixel.hytale.protocol.Rangef;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.HarvestingDropType;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingData;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingStageData;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.GrowthModifierAsset;
|
||||
import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset;
|
||||
import com.hypixel.hytale.server.core.entity.ItemUtils;
|
||||
import com.hypixel.hytale.server.core.modules.block.BlockModule;
|
||||
import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.BlockHarvestUtils;
|
||||
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import com.hypixel.hytale.server.npc.metadata.CapturedNPCMetadata;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class FarmingUtil {
|
||||
private static final int MAX_SECONDS_BETWEEN_TICKS = 15;
|
||||
private static final int BETWEEN_RANDOM = 10;
|
||||
|
||||
public FarmingUtil() {
|
||||
}
|
||||
|
||||
public static void tickFarming(
|
||||
CommandBuffer<ChunkStore> commandBuffer,
|
||||
BlockChunk blockChunk,
|
||||
BlockSection blockSection,
|
||||
Ref<ChunkStore> sectionRef,
|
||||
Ref<ChunkStore> blockRef,
|
||||
FarmingBlock farmingBlock,
|
||||
int x,
|
||||
int y,
|
||||
int z,
|
||||
boolean initialTick
|
||||
) {
|
||||
World world = commandBuffer.getExternalData().getWorld();
|
||||
WorldTimeResource worldTimeResource = world.getEntityStore().getStore().getResource(WorldTimeResource.getResourceType());
|
||||
Instant currentTime = worldTimeResource.getGameTime();
|
||||
BlockType blockType = farmingBlock.getPreviousBlockType() != null
|
||||
? BlockType.getAssetMap().getAsset(farmingBlock.getPreviousBlockType())
|
||||
: BlockType.getAssetMap().getAsset(blockSection.get(x, y, z));
|
||||
if (blockType != null) {
|
||||
if (blockType.getFarming() != null) {
|
||||
FarmingData farmingConfig = blockType.getFarming();
|
||||
if (farmingConfig.getStages() != null) {
|
||||
float currentProgress = farmingBlock.getGrowthProgress();
|
||||
int currentStage = (int)currentProgress;
|
||||
String currentStageSet = farmingBlock.getCurrentStageSet();
|
||||
FarmingStageData[] stages = currentStageSet != null ? farmingConfig.getStages().get(currentStageSet) : null;
|
||||
if (stages == null) {
|
||||
currentStageSet = farmingConfig.getStartingStageSet();
|
||||
if (currentStageSet == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
farmingBlock.setCurrentStageSet(currentStageSet);
|
||||
stages = farmingConfig.getStages().get(currentStageSet);
|
||||
blockChunk.markNeedsSaving();
|
||||
}
|
||||
|
||||
if (stages != null) {
|
||||
if (currentStage < 0) {
|
||||
currentStage = 0;
|
||||
currentProgress = 0.0F;
|
||||
farmingBlock.setGrowthProgress(0.0F);
|
||||
}
|
||||
|
||||
if (currentStage >= stages.length) {
|
||||
commandBuffer.removeEntity(blockRef, RemoveReason.REMOVE);
|
||||
} else {
|
||||
long remainingTimeSeconds = currentTime.getEpochSecond() - farmingBlock.getLastTickGameTime().getEpochSecond();
|
||||
ChunkSection section = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
|
||||
int worldX = ChunkUtil.worldCoordFromLocalCoord(section.getX(), x);
|
||||
int worldY = ChunkUtil.worldCoordFromLocalCoord(section.getY(), y);
|
||||
int worldZ = ChunkUtil.worldCoordFromLocalCoord(section.getZ(), z);
|
||||
|
||||
while (currentStage < stages.length) {
|
||||
FarmingStageData stage = stages[currentStage];
|
||||
if (stage.shouldStop(commandBuffer, sectionRef, blockRef, x, y, z)) {
|
||||
blockChunk.markNeedsSaving();
|
||||
farmingBlock.setGrowthProgress(stages.length);
|
||||
commandBuffer.removeEntity(blockRef, RemoveReason.REMOVE);
|
||||
break;
|
||||
}
|
||||
|
||||
Rangef range = stage.getDuration();
|
||||
if (range == null) {
|
||||
blockChunk.markNeedsSaving();
|
||||
commandBuffer.removeEntity(blockRef, RemoveReason.REMOVE);
|
||||
break;
|
||||
}
|
||||
|
||||
double rand = HashUtil.random(farmingBlock.getGeneration(), worldX, worldY, worldZ);
|
||||
double baseDuration = range.min + (range.max - range.min) * rand;
|
||||
long remainingDurationSeconds = Math.round(baseDuration * (1.0 - currentProgress % 1.0));
|
||||
double growthMultiplier = 1.0;
|
||||
if (farmingConfig.getGrowthModifiers() != null) {
|
||||
for (String modifierName : farmingConfig.getGrowthModifiers()) {
|
||||
GrowthModifierAsset modifier = GrowthModifierAsset.getAssetMap().getAsset(modifierName);
|
||||
if (modifier != null) {
|
||||
growthMultiplier *= modifier.getCurrentGrowthMultiplier(commandBuffer, sectionRef, blockRef, x, y, z, initialTick);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remainingDurationSeconds = Math.round(remainingDurationSeconds / growthMultiplier);
|
||||
if (remainingTimeSeconds < remainingDurationSeconds) {
|
||||
currentProgress += (float)(remainingTimeSeconds / (baseDuration / growthMultiplier));
|
||||
farmingBlock.setGrowthProgress(currentProgress);
|
||||
long nextGrowthInNanos = (remainingDurationSeconds - remainingTimeSeconds) * 1000000000L;
|
||||
long randCap = (long)(
|
||||
(15.0 + 10.0 * HashUtil.random(farmingBlock.getGeneration() ^ 3405692655L, worldX, worldY, worldZ))
|
||||
* world.getTps()
|
||||
* WorldTimeResource.getSecondsPerTick(world)
|
||||
* 1.0E9
|
||||
);
|
||||
long cappedNextGrowthInNanos = Math.min(nextGrowthInNanos, randCap);
|
||||
blockSection.scheduleTick(ChunkUtil.indexBlock(x, y, z), currentTime.plusNanos(cappedNextGrowthInNanos));
|
||||
break;
|
||||
}
|
||||
|
||||
remainingTimeSeconds -= remainingDurationSeconds;
|
||||
currentProgress = ++currentStage;
|
||||
farmingBlock.setGrowthProgress(currentProgress);
|
||||
blockChunk.markNeedsSaving();
|
||||
farmingBlock.setGeneration(farmingBlock.getGeneration() + 1);
|
||||
if (currentStage >= stages.length) {
|
||||
if (stages[currentStage - 1].implementsShouldStop()) {
|
||||
currentStage = stages.length - 1;
|
||||
farmingBlock.setGrowthProgress(currentStage);
|
||||
stages[currentStage].apply(commandBuffer, sectionRef, blockRef, x, y, z, stages[currentStage]);
|
||||
} else {
|
||||
farmingBlock.setGrowthProgress(stages.length);
|
||||
commandBuffer.removeEntity(blockRef, RemoveReason.REMOVE);
|
||||
}
|
||||
} else {
|
||||
farmingBlock.setExecutions(0);
|
||||
stages[currentStage].apply(commandBuffer, sectionRef, blockRef, x, y, z, stages[currentStage - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
farmingBlock.setLastTickGameTime(currentTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void harvest(
|
||||
@Nonnull World world,
|
||||
@Nonnull ComponentAccessor<EntityStore> store,
|
||||
@Nonnull Ref<EntityStore> ref,
|
||||
@Nonnull BlockType blockType,
|
||||
int rotationIndex,
|
||||
@Nonnull Vector3i blockPosition
|
||||
) {
|
||||
if (world.getGameplayConfig().getWorldConfig().isBlockGatheringAllowed()) {
|
||||
harvest0(store, ref, blockType, rotationIndex, blockPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static CapturedNPCMetadata generateCapturedNPCMetadata(
|
||||
@Nonnull ComponentAccessor<EntityStore> componentAccessor, @Nonnull Ref<EntityStore> entityRef, int roleIndex
|
||||
) {
|
||||
PersistentModel persistentModel = componentAccessor.getComponent(entityRef, PersistentModel.getComponentType());
|
||||
if (persistentModel == null) {
|
||||
return null;
|
||||
} else {
|
||||
ModelAsset modelAsset = ModelAsset.getAssetMap().getAsset(persistentModel.getModelReference().getModelAssetId());
|
||||
CapturedNPCMetadata meta = new CapturedNPCMetadata();
|
||||
if (modelAsset != null) {
|
||||
meta.setIconPath(modelAsset.getIcon());
|
||||
}
|
||||
|
||||
meta.setRoleIndex(roleIndex);
|
||||
return meta;
|
||||
}
|
||||
}
|
||||
|
||||
protected static boolean harvest0(
|
||||
@Nonnull ComponentAccessor<EntityStore> store,
|
||||
@Nonnull Ref<EntityStore> ref,
|
||||
@Nonnull BlockType blockType,
|
||||
int rotationIndex,
|
||||
@Nonnull Vector3i blockPosition
|
||||
) {
|
||||
FarmingData farmingConfig = blockType.getFarming();
|
||||
if (farmingConfig == null || farmingConfig.getStages() == null) {
|
||||
return false;
|
||||
} else if (blockType.getGathering().getHarvest() == null) {
|
||||
return false;
|
||||
} else {
|
||||
World world = store.getExternalData().getWorld();
|
||||
Vector3d centerPosition = new Vector3d();
|
||||
blockType.getBlockCenter(rotationIndex, centerPosition);
|
||||
centerPosition.add(blockPosition);
|
||||
if (farmingConfig.getStageSetAfterHarvest() == null) {
|
||||
giveDrops(store, ref, centerPosition, blockType);
|
||||
WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(blockPosition.x, blockPosition.z));
|
||||
if (chunk != null) {
|
||||
chunk.breakBlock(blockPosition.x, blockPosition.y, blockPosition.z);
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
giveDrops(store, ref, centerPosition, blockType);
|
||||
Map<String, FarmingStageData[]> stageSets = farmingConfig.getStages();
|
||||
FarmingStageData[] stages = stageSets.get(farmingConfig.getStartingStageSet());
|
||||
if (stages == null) {
|
||||
return false;
|
||||
} else {
|
||||
int currentStageIndex = stages.length - 1;
|
||||
FarmingStageData previousStage = stages[currentStageIndex];
|
||||
String newStageSet = farmingConfig.getStageSetAfterHarvest();
|
||||
FarmingStageData[] newStages = stageSets.get(newStageSet);
|
||||
if (newStages != null && newStages.length != 0) {
|
||||
Store<ChunkStore> chunkStore = world.getChunkStore().getStore();
|
||||
Ref<ChunkStore> chunkRef = world.getChunkStore().getChunkReference(ChunkUtil.indexChunkFromBlock(blockPosition.x, blockPosition.z));
|
||||
if (chunkRef == null) {
|
||||
return false;
|
||||
} else {
|
||||
BlockComponentChunk blockComponentChunk = chunkStore.getComponent(chunkRef, BlockComponentChunk.getComponentType());
|
||||
if (blockComponentChunk == null) {
|
||||
return false;
|
||||
} else {
|
||||
Instant now = store.getExternalData()
|
||||
.getWorld()
|
||||
.getEntityStore()
|
||||
.getStore()
|
||||
.getResource(WorldTimeResource.getResourceType())
|
||||
.getGameTime();
|
||||
int blockIndexColumn = ChunkUtil.indexBlockInColumn(blockPosition.x, blockPosition.y, blockPosition.z);
|
||||
Ref<ChunkStore> blockRef = blockComponentChunk.getEntityReference(blockIndexColumn);
|
||||
FarmingBlock farmingBlock;
|
||||
if (blockRef == null) {
|
||||
Holder<ChunkStore> blockEntity = ChunkStore.REGISTRY.newHolder();
|
||||
blockEntity.putComponent(BlockModule.BlockStateInfo.getComponentType(), new BlockModule.BlockStateInfo(blockIndexColumn, chunkRef));
|
||||
farmingBlock = new FarmingBlock();
|
||||
farmingBlock.setLastTickGameTime(now);
|
||||
farmingBlock.setCurrentStageSet(newStageSet);
|
||||
blockEntity.addComponent(FarmingBlock.getComponentType(), farmingBlock);
|
||||
blockRef = chunkStore.addEntity(blockEntity, AddReason.SPAWN);
|
||||
} else {
|
||||
farmingBlock = chunkStore.ensureAndGetComponent(blockRef, FarmingBlock.getComponentType());
|
||||
}
|
||||
|
||||
farmingBlock.setCurrentStageSet(newStageSet);
|
||||
farmingBlock.setGrowthProgress(0.0F);
|
||||
farmingBlock.setExecutions(0);
|
||||
farmingBlock.setGeneration(farmingBlock.getGeneration() + 1);
|
||||
farmingBlock.setLastTickGameTime(now);
|
||||
Ref<ChunkStore> sectionRef = world.getChunkStore()
|
||||
.getChunkSectionReference(
|
||||
ChunkUtil.chunkCoordinate(blockPosition.x),
|
||||
ChunkUtil.chunkCoordinate(blockPosition.y),
|
||||
ChunkUtil.chunkCoordinate(blockPosition.z)
|
||||
);
|
||||
if (sectionRef == null) {
|
||||
return false;
|
||||
} else if (blockRef == null) {
|
||||
return false;
|
||||
} else {
|
||||
BlockSection section = chunkStore.getComponent(sectionRef, BlockSection.getComponentType());
|
||||
if (section != null) {
|
||||
section.scheduleTick(ChunkUtil.indexBlock(blockPosition.x, blockPosition.y, blockPosition.z), now);
|
||||
}
|
||||
|
||||
newStages[0].apply(chunkStore, sectionRef, blockRef, blockPosition.x, blockPosition.y, blockPosition.z, previousStage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
WorldChunk chunk = world.getChunkIfInMemory(ChunkUtil.indexChunkFromBlock(blockPosition.x, blockPosition.z));
|
||||
if (chunk != null) {
|
||||
chunk.breakBlock(blockPosition.x, blockPosition.y, blockPosition.z);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static void giveDrops(
|
||||
@Nonnull ComponentAccessor<EntityStore> store, @Nonnull Ref<EntityStore> ref, @Nonnull Vector3d origin, @Nonnull BlockType blockType
|
||||
) {
|
||||
HarvestingDropType harvest = blockType.getGathering().getHarvest();
|
||||
String itemId = harvest.getItemId();
|
||||
String dropListId = harvest.getDropListId();
|
||||
BlockHarvestUtils.getDrops(blockType, 1, itemId, dropListId).forEach(itemStack -> ItemUtils.interactivelyPickupItem(ref, itemStack, origin, store));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.component;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.FarmingPlugin;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.component.Component;
|
||||
import com.hypixel.hytale.component.ComponentType;
|
||||
import com.hypixel.hytale.math.vector.Vector3i;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class CoopResidentComponent implements Component<EntityStore> {
|
||||
public static final BuilderCodec<CoopResidentComponent> CODEC = BuilderCodec.builder(CoopResidentComponent.class, CoopResidentComponent::new)
|
||||
.append(new KeyedCodec<>("CoopLocation", Vector3i.CODEC), (comp, ref) -> comp.coopLocation = ref, comp -> comp.coopLocation)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("MarkedForDespawn", BuilderCodec.BOOLEAN),
|
||||
(comp, markedForDespawn) -> comp.markedForDespawn = markedForDespawn,
|
||||
comp -> comp.markedForDespawn
|
||||
)
|
||||
.add()
|
||||
.build();
|
||||
private Vector3i coopLocation = new Vector3i();
|
||||
private boolean markedForDespawn;
|
||||
|
||||
public CoopResidentComponent() {
|
||||
}
|
||||
|
||||
public static ComponentType<EntityStore, CoopResidentComponent> getComponentType() {
|
||||
return FarmingPlugin.get().getCoopResidentComponentType();
|
||||
}
|
||||
|
||||
public void setCoopLocation(Vector3i coopLocation) {
|
||||
this.coopLocation = coopLocation;
|
||||
}
|
||||
|
||||
public Vector3i getCoopLocation() {
|
||||
return this.coopLocation;
|
||||
}
|
||||
|
||||
public void setMarkedForDespawn(boolean markedForDespawn) {
|
||||
this.markedForDespawn = markedForDespawn;
|
||||
}
|
||||
|
||||
public boolean getMarkedForDespawn() {
|
||||
return this.markedForDespawn;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Component<EntityStore> clone() {
|
||||
CoopResidentComponent component = new CoopResidentComponent();
|
||||
component.coopLocation.assign(this.coopLocation);
|
||||
component.markedForDespawn = this.markedForDespawn;
|
||||
return component;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.config;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetExtraInfo;
|
||||
import com.hypixel.hytale.assetstore.AssetRegistry;
|
||||
import com.hypixel.hytale.assetstore.AssetStore;
|
||||
import com.hypixel.hytale.assetstore.codec.AssetBuilderCodec;
|
||||
import com.hypixel.hytale.assetstore.map.DefaultAssetMap;
|
||||
import com.hypixel.hytale.assetstore.map.JsonAssetWithMap;
|
||||
import com.hypixel.hytale.builtin.tagset.config.NPCGroup;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.codecs.map.MapCodec;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.math.range.IntRange;
|
||||
import com.hypixel.hytale.math.vector.Vector3d;
|
||||
import com.hypixel.hytale.server.core.asset.type.item.config.ItemDropList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class FarmingCoopAsset implements JsonAssetWithMap<String, DefaultAssetMap<String, FarmingCoopAsset>> {
|
||||
public static final AssetBuilderCodec<String, FarmingCoopAsset> CODEC = AssetBuilderCodec.builder(
|
||||
FarmingCoopAsset.class, FarmingCoopAsset::new, Codec.STRING, (o, v) -> o.id = v, FarmingCoopAsset::getId, (o, data) -> o.data = data, o -> o.data
|
||||
)
|
||||
.appendInherited(
|
||||
new KeyedCodec<>("MaxResidents", Codec.INTEGER),
|
||||
(asset, maxResidents) -> asset.maxResidents = maxResidents,
|
||||
asset -> asset.maxResidents,
|
||||
(asset, parent) -> asset.maxResidents = parent.maxResidents
|
||||
)
|
||||
.add()
|
||||
.<Map>append(
|
||||
new KeyedCodec<>("ProduceDrops", new MapCodec<>(ItemDropList.CHILD_ASSET_CODEC, HashMap::new)),
|
||||
(asset, drops) -> asset.produceDrops = drops,
|
||||
asset -> asset.produceDrops
|
||||
)
|
||||
.addValidator(ItemDropList.VALIDATOR_CACHE.getMapValueValidator())
|
||||
.add()
|
||||
.<Vector3d>append(
|
||||
new KeyedCodec<>("ResidentSpawnOffset", Vector3d.CODEC),
|
||||
(asset, residentSpawnOffset) -> asset.residentSpawnOffset.assign(residentSpawnOffset),
|
||||
asset -> asset.residentSpawnOffset
|
||||
)
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("ResidentRoamTime", IntRange.CODEC),
|
||||
(asset, residentRoamTime) -> asset.residentRoamTime = residentRoamTime,
|
||||
asset -> asset.residentRoamTime
|
||||
)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("CaptureWildNPCsInRange", Codec.BOOLEAN),
|
||||
(asset, captureWildNPCsInRange) -> asset.captureWildNPCsInRange = captureWildNPCsInRange,
|
||||
asset -> asset.captureWildNPCsInRange
|
||||
)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("WildCaptureRadius", Codec.FLOAT),
|
||||
(asset, wildCaptureRadius) -> asset.wildCaptureRadius = wildCaptureRadius,
|
||||
asset -> asset.wildCaptureRadius
|
||||
)
|
||||
.add()
|
||||
.<String[]>appendInherited(
|
||||
new KeyedCodec<>("AcceptedNpcGroups", NPCGroup.CHILD_ASSET_CODEC_ARRAY),
|
||||
(o, v) -> o.acceptedNpcGroupIds = v,
|
||||
o -> o.acceptedNpcGroupIds,
|
||||
(o, p) -> o.acceptedNpcGroupIds = p.acceptedNpcGroupIds
|
||||
)
|
||||
.addValidator(NPCGroup.VALIDATOR_CACHE.getArrayValidator())
|
||||
.add()
|
||||
.afterDecode(captureData -> {
|
||||
if (captureData.acceptedNpcGroupIds != null) {
|
||||
captureData.acceptedNpcGroupIndexes = new int[captureData.acceptedNpcGroupIds.length];
|
||||
|
||||
for (int i = 0; i < captureData.acceptedNpcGroupIds.length; i++) {
|
||||
int assetIdx = NPCGroup.getAssetMap().getIndex(captureData.acceptedNpcGroupIds[i]);
|
||||
captureData.acceptedNpcGroupIndexes[i] = assetIdx;
|
||||
}
|
||||
}
|
||||
})
|
||||
.build();
|
||||
private static AssetStore<String, FarmingCoopAsset, DefaultAssetMap<String, FarmingCoopAsset>> ASSET_STORE;
|
||||
private AssetExtraInfo.Data data;
|
||||
protected String id;
|
||||
protected int maxResidents;
|
||||
protected Map<String, String> produceDrops = Collections.emptyMap();
|
||||
protected IntRange residentRoamTime;
|
||||
protected Vector3d residentSpawnOffset = new Vector3d();
|
||||
protected String[] acceptedNpcGroupIds;
|
||||
protected int[] acceptedNpcGroupIndexes;
|
||||
protected boolean captureWildNPCsInRange;
|
||||
protected float wildCaptureRadius;
|
||||
|
||||
public static AssetStore<String, FarmingCoopAsset, DefaultAssetMap<String, FarmingCoopAsset>> getAssetStore() {
|
||||
if (ASSET_STORE == null) {
|
||||
ASSET_STORE = AssetRegistry.getAssetStore(FarmingCoopAsset.class);
|
||||
}
|
||||
|
||||
return ASSET_STORE;
|
||||
}
|
||||
|
||||
public static DefaultAssetMap<String, FarmingCoopAsset> getAssetMap() {
|
||||
return (DefaultAssetMap<String, FarmingCoopAsset>)getAssetStore().getAssetMap();
|
||||
}
|
||||
|
||||
public FarmingCoopAsset() {
|
||||
}
|
||||
|
||||
public FarmingCoopAsset(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public Map<String, String> getProduceDrops() {
|
||||
return this.produceDrops;
|
||||
}
|
||||
|
||||
public int getMaxResidents() {
|
||||
return this.maxResidents;
|
||||
}
|
||||
|
||||
public IntRange getResidentRoamTime() {
|
||||
return this.residentRoamTime;
|
||||
}
|
||||
|
||||
public Vector3d getResidentSpawnOffset() {
|
||||
return this.residentSpawnOffset;
|
||||
}
|
||||
|
||||
public int[] getAcceptedNpcGroupIndexes() {
|
||||
return this.acceptedNpcGroupIndexes;
|
||||
}
|
||||
|
||||
public float getWildCaptureRadius() {
|
||||
return this.wildCaptureRadius;
|
||||
}
|
||||
|
||||
public boolean getCaptureWildNPCsInRange() {
|
||||
return this.captureWildNPCsInRange;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FarmingCoopAsset{id='" + this.id + "', maxResidents=" + this.maxResidents + "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.config.modifiers;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.TilledSoilBlock;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.GrowthModifierAsset;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
|
||||
public class FertilizerGrowthModifierAsset extends GrowthModifierAsset {
|
||||
public static final BuilderCodec<FertilizerGrowthModifierAsset> CODEC = BuilderCodec.builder(
|
||||
FertilizerGrowthModifierAsset.class, FertilizerGrowthModifierAsset::new, ABSTRACT_CODEC
|
||||
)
|
||||
.build();
|
||||
|
||||
public FertilizerGrowthModifierAsset() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCurrentGrowthMultiplier(
|
||||
CommandBuffer<ChunkStore> commandBuffer, Ref<ChunkStore> sectionRef, Ref<ChunkStore> blockRef, int x, int y, int z, boolean initialTick
|
||||
) {
|
||||
ChunkSection chunkSection = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
|
||||
Ref<ChunkStore> chunk = chunkSection.getChunkColumnReference();
|
||||
BlockComponentChunk blockComponentChunk = commandBuffer.getComponent(chunk, BlockComponentChunk.getComponentType());
|
||||
Ref<ChunkStore> blockRefBelow = blockComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(x, y - 1, z));
|
||||
if (blockRefBelow == null) {
|
||||
return 1.0;
|
||||
} else {
|
||||
TilledSoilBlock soil = commandBuffer.getComponent(blockRefBelow, TilledSoilBlock.getComponentType());
|
||||
return soil != null && soil.isFertilized() ? super.getCurrentGrowthMultiplier(commandBuffer, sectionRef, blockRef, x, y, z, initialTick) : 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.config.modifiers;
|
||||
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.protocol.Range;
|
||||
import com.hypixel.hytale.protocol.Rangef;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.GrowthModifierAsset;
|
||||
import com.hypixel.hytale.server.core.codec.ProtocolCodecs;
|
||||
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkLightData;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class LightLevelGrowthModifierAsset extends GrowthModifierAsset {
|
||||
public static final BuilderCodec<LightLevelGrowthModifierAsset> CODEC = BuilderCodec.builder(
|
||||
LightLevelGrowthModifierAsset.class, LightLevelGrowthModifierAsset::new, ABSTRACT_CODEC
|
||||
)
|
||||
.addField(
|
||||
new KeyedCodec<>("ArtificialLight", LightLevelGrowthModifierAsset.ArtificialLight.CODEC),
|
||||
(lightLevel, artificialLight) -> lightLevel.artificialLight = artificialLight,
|
||||
lightLevel -> lightLevel.artificialLight
|
||||
)
|
||||
.addField(
|
||||
new KeyedCodec<>("Sunlight", ProtocolCodecs.RANGEF), (lightLevel, sunLight) -> lightLevel.sunlight = sunLight, lightLevel -> lightLevel.sunlight
|
||||
)
|
||||
.addField(
|
||||
new KeyedCodec<>("RequireBoth", Codec.BOOLEAN),
|
||||
(lightLevel, requireBoth) -> lightLevel.requireBoth = requireBoth,
|
||||
lightLevel -> lightLevel.requireBoth
|
||||
)
|
||||
.build();
|
||||
protected LightLevelGrowthModifierAsset.ArtificialLight artificialLight;
|
||||
protected Rangef sunlight;
|
||||
protected boolean requireBoth;
|
||||
|
||||
public LightLevelGrowthModifierAsset() {
|
||||
}
|
||||
|
||||
public LightLevelGrowthModifierAsset.ArtificialLight getArtificialLight() {
|
||||
return this.artificialLight;
|
||||
}
|
||||
|
||||
public Rangef getSunlight() {
|
||||
return this.sunlight;
|
||||
}
|
||||
|
||||
public boolean isRequireBoth() {
|
||||
return this.requireBoth;
|
||||
}
|
||||
|
||||
protected boolean checkArtificialLight(byte red, byte green, byte blue) {
|
||||
LightLevelGrowthModifierAsset.ArtificialLight artificialLight = this.artificialLight;
|
||||
Range redRange = artificialLight.getRed();
|
||||
Range greenRange = artificialLight.getGreen();
|
||||
Range blueRange = artificialLight.getBlue();
|
||||
return isInRange(redRange, red) && isInRange(greenRange, green) && isInRange(blueRange, blue);
|
||||
}
|
||||
|
||||
protected boolean checkSunLight(WorldTimeResource worldTimeResource, byte sky) {
|
||||
Rangef range = this.sunlight;
|
||||
double sunlightFactor = worldTimeResource.getSunlightFactor();
|
||||
double daylight = sunlightFactor * sky;
|
||||
return range.min <= daylight && daylight <= range.max;
|
||||
}
|
||||
|
||||
protected static boolean isInRange(@Nonnull Range range, int value) {
|
||||
return range.min <= value && value <= range.max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCurrentGrowthMultiplier(
|
||||
CommandBuffer<ChunkStore> commandBuffer, Ref<ChunkStore> sectionRef, Ref<ChunkStore> blockRef, int x, int y, int z, boolean initialTick
|
||||
) {
|
||||
BlockSection blockSection = commandBuffer.getComponent(sectionRef, BlockSection.getComponentType());
|
||||
short lightRaw = blockSection.getGlobalLight().getLightRaw(x, y, z);
|
||||
byte redLight = ChunkLightData.getLightValue(lightRaw, 0);
|
||||
byte greenLight = ChunkLightData.getLightValue(lightRaw, 1);
|
||||
byte blueLight = ChunkLightData.getLightValue(lightRaw, 2);
|
||||
byte skyLight = ChunkLightData.getLightValue(lightRaw, 3);
|
||||
WorldTimeResource worldTimeResource = commandBuffer.getExternalData()
|
||||
.getWorld()
|
||||
.getEntityStore()
|
||||
.getStore()
|
||||
.getResource(WorldTimeResource.getResourceType());
|
||||
boolean active = false;
|
||||
boolean onlySunlight = false;
|
||||
if (this.requireBoth) {
|
||||
active = this.checkArtificialLight(redLight, greenLight, blueLight) && this.checkSunLight(worldTimeResource, skyLight);
|
||||
} else if (this.checkArtificialLight(redLight, greenLight, blueLight)) {
|
||||
active = true;
|
||||
} else if (this.checkSunLight(worldTimeResource, skyLight)) {
|
||||
active = true;
|
||||
onlySunlight = true;
|
||||
}
|
||||
|
||||
if (active) {
|
||||
return onlySunlight && initialTick
|
||||
? super.getCurrentGrowthMultiplier(commandBuffer, sectionRef, blockRef, x, y, z, initialTick) * 0.6F
|
||||
: super.getCurrentGrowthMultiplier(commandBuffer, sectionRef, blockRef, x, y, z, initialTick);
|
||||
} else {
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LightLevelGrowthModifierAsset{artificialLight="
|
||||
+ this.artificialLight
|
||||
+ ", sunLight="
|
||||
+ this.sunlight
|
||||
+ ", requireBoth="
|
||||
+ this.requireBoth
|
||||
+ "} "
|
||||
+ super.toString();
|
||||
}
|
||||
|
||||
public static class ArtificialLight {
|
||||
public static final BuilderCodec<LightLevelGrowthModifierAsset.ArtificialLight> CODEC = BuilderCodec.builder(
|
||||
LightLevelGrowthModifierAsset.ArtificialLight.class, LightLevelGrowthModifierAsset.ArtificialLight::new
|
||||
)
|
||||
.addField(new KeyedCodec<>("Red", ProtocolCodecs.RANGE), (light, red) -> light.red = red, light -> light.red)
|
||||
.addField(new KeyedCodec<>("Green", ProtocolCodecs.RANGE), (light, green) -> light.green = green, light -> light.green)
|
||||
.addField(new KeyedCodec<>("Blue", ProtocolCodecs.RANGE), (light, blue) -> light.blue = blue, light -> light.blue)
|
||||
.build();
|
||||
protected Range red;
|
||||
protected Range green;
|
||||
protected Range blue;
|
||||
|
||||
public ArtificialLight() {
|
||||
}
|
||||
|
||||
public Range getRed() {
|
||||
return this.red;
|
||||
}
|
||||
|
||||
public Range getGreen() {
|
||||
return this.green;
|
||||
}
|
||||
|
||||
public Range getBlue() {
|
||||
return this.blue;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ArtificialLightLevel{red=" + this.red + ", green=" + this.green + ", blue=" + this.blue + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,235 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.config.modifiers;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.TilledSoilBlock;
|
||||
import com.hypixel.hytale.builtin.weather.resources.WeatherResource;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.GrowthModifierAsset;
|
||||
import com.hypixel.hytale.server.core.asset.type.fluid.Fluid;
|
||||
import com.hypixel.hytale.server.core.asset.type.weather.config.Weather;
|
||||
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class WaterGrowthModifierAsset extends GrowthModifierAsset {
|
||||
public static final BuilderCodec<WaterGrowthModifierAsset> CODEC = BuilderCodec.builder(
|
||||
WaterGrowthModifierAsset.class, WaterGrowthModifierAsset::new, ABSTRACT_CODEC
|
||||
)
|
||||
.append(new KeyedCodec<>("Fluids", new ArrayCodec<>(Codec.STRING, String[]::new)), (asset, blocks) -> asset.fluids = blocks, asset -> asset.fluids)
|
||||
.addValidator(Fluid.VALIDATOR_CACHE.getArrayValidator().late())
|
||||
.add()
|
||||
.<String[]>append(new KeyedCodec<>("Weathers", Codec.STRING_ARRAY), (asset, weathers) -> asset.weathers = weathers, asset -> asset.weathers)
|
||||
.addValidator(Weather.VALIDATOR_CACHE.getArrayValidator())
|
||||
.add()
|
||||
.addField(new KeyedCodec<>("RainDuration", Codec.INTEGER), (asset, duration) -> asset.rainDuration = duration, asset -> asset.rainDuration)
|
||||
.afterDecode(asset -> {
|
||||
if (asset.fluids != null) {
|
||||
asset.fluidIds = new IntOpenHashSet();
|
||||
|
||||
for (int i = 0; i < asset.fluids.length; i++) {
|
||||
asset.fluidIds.add(Fluid.getAssetMap().getIndex(asset.fluids[i]));
|
||||
}
|
||||
}
|
||||
|
||||
if (asset.weathers != null) {
|
||||
asset.weatherIds = new IntOpenHashSet();
|
||||
|
||||
for (int i = 0; i < asset.weathers.length; i++) {
|
||||
asset.weatherIds.add(Weather.getAssetMap().getIndex(asset.weathers[i]));
|
||||
}
|
||||
}
|
||||
})
|
||||
.build();
|
||||
protected String[] fluids;
|
||||
protected IntOpenHashSet fluidIds;
|
||||
protected String[] weathers;
|
||||
protected IntOpenHashSet weatherIds;
|
||||
protected int rainDuration;
|
||||
|
||||
public WaterGrowthModifierAsset() {
|
||||
}
|
||||
|
||||
public String[] getFluids() {
|
||||
return this.fluids;
|
||||
}
|
||||
|
||||
public IntOpenHashSet getFluidIds() {
|
||||
return this.fluidIds;
|
||||
}
|
||||
|
||||
public String[] getWeathers() {
|
||||
return this.weathers;
|
||||
}
|
||||
|
||||
public IntOpenHashSet getWeatherIds() {
|
||||
return this.weatherIds;
|
||||
}
|
||||
|
||||
public int getRainDuration() {
|
||||
return this.rainDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCurrentGrowthMultiplier(
|
||||
CommandBuffer<ChunkStore> commandBuffer, Ref<ChunkStore> sectionRef, Ref<ChunkStore> blockRef, int x, int y, int z, boolean initialTick
|
||||
) {
|
||||
boolean hasWaterBlock = this.checkIfWaterSource(commandBuffer, sectionRef, blockRef, x, y, z);
|
||||
boolean isRaining = this.checkIfRaining(commandBuffer, sectionRef, x, y, z);
|
||||
boolean active = hasWaterBlock || isRaining;
|
||||
TilledSoilBlock soil = getSoil(commandBuffer, sectionRef, x, y, z);
|
||||
if (soil != null) {
|
||||
if (soil.hasExternalWater() != active) {
|
||||
soil.setExternalWater(active);
|
||||
commandBuffer.getComponent(sectionRef, BlockSection.getComponentType()).setTicking(x, y, z, true);
|
||||
}
|
||||
|
||||
active |= this.isSoilWaterExpiring(
|
||||
commandBuffer.getExternalData().getWorld().getEntityStore().getStore().getResource(WorldTimeResource.getResourceType()), soil
|
||||
);
|
||||
}
|
||||
|
||||
return !active ? 1.0 : super.getCurrentGrowthMultiplier(commandBuffer, sectionRef, blockRef, x, y, z, initialTick);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static TilledSoilBlock getSoil(CommandBuffer<ChunkStore> commandBuffer, Ref<ChunkStore> sectionRef, int x, int y, int z) {
|
||||
ChunkSection chunkSection = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
|
||||
Ref<ChunkStore> chunk = chunkSection.getChunkColumnReference();
|
||||
BlockComponentChunk blockComponentChunk = commandBuffer.getComponent(chunk, BlockComponentChunk.getComponentType());
|
||||
Ref<ChunkStore> blockRefBelow = blockComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(x, y - 1, z));
|
||||
return blockRefBelow == null ? null : commandBuffer.getComponent(blockRefBelow, TilledSoilBlock.getComponentType());
|
||||
}
|
||||
|
||||
protected boolean checkIfWaterSource(CommandBuffer<ChunkStore> commandBuffer, Ref<ChunkStore> sectionRef, Ref<ChunkStore> blockRef, int x, int y, int z) {
|
||||
IntOpenHashSet waterBlocks = this.fluidIds;
|
||||
if (waterBlocks == null) {
|
||||
return false;
|
||||
} else {
|
||||
TilledSoilBlock soil = getSoil(commandBuffer, sectionRef, x, y, z);
|
||||
if (soil == null) {
|
||||
return false;
|
||||
} else {
|
||||
int[] fluids = this.getNeighbourFluids(commandBuffer, sectionRef, x, y - 1, z);
|
||||
|
||||
for (int block : fluids) {
|
||||
if (waterBlocks.contains(block)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int[] getNeighbourFluids(CommandBuffer<ChunkStore> commandBuffer, Ref<ChunkStore> sectionRef, int x, int y, int z) {
|
||||
ChunkSection section = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
|
||||
return new int[]{
|
||||
this.getFluidAtPos(x - 1, y, z, sectionRef, section, commandBuffer),
|
||||
this.getFluidAtPos(x + 1, y, z, sectionRef, section, commandBuffer),
|
||||
this.getFluidAtPos(x, y, z - 1, sectionRef, section, commandBuffer),
|
||||
this.getFluidAtPos(x, y, z + 1, sectionRef, section, commandBuffer)
|
||||
};
|
||||
}
|
||||
|
||||
private int getFluidAtPos(
|
||||
int posX, int posY, int posZ, Ref<ChunkStore> sectionRef, ChunkSection currentChunkSection, CommandBuffer<ChunkStore> commandBuffer
|
||||
) {
|
||||
Ref<ChunkStore> chunkToUse = sectionRef;
|
||||
int chunkX = ChunkUtil.worldCoordFromLocalCoord(currentChunkSection.getX(), posX);
|
||||
int chunkY = ChunkUtil.worldCoordFromLocalCoord(currentChunkSection.getY(), posY);
|
||||
int chunkZ = ChunkUtil.worldCoordFromLocalCoord(currentChunkSection.getZ(), posZ);
|
||||
if (ChunkUtil.isSameChunkSection(chunkX, chunkY, chunkZ, currentChunkSection.getX(), currentChunkSection.getY(), currentChunkSection.getZ())) {
|
||||
chunkToUse = commandBuffer.getExternalData().getChunkSectionReference(chunkX, chunkY, chunkZ);
|
||||
}
|
||||
|
||||
return chunkToUse == null ? Integer.MIN_VALUE : commandBuffer.getComponent(chunkToUse, FluidSection.getComponentType()).getFluidId(posX, posY, posZ);
|
||||
}
|
||||
|
||||
protected boolean checkIfRaining(CommandBuffer<ChunkStore> commandBuffer, Ref<ChunkStore> sectionRef, int x, int y, int z) {
|
||||
if (this.weatherIds == null) {
|
||||
return false;
|
||||
} else {
|
||||
ChunkSection section = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
|
||||
Ref<ChunkStore> chunk = section.getChunkColumnReference();
|
||||
BlockChunk blockChunk = commandBuffer.getComponent(chunk, BlockChunk.getComponentType());
|
||||
int cropId = blockChunk.getBlock(x, y, z);
|
||||
Store<EntityStore> store = commandBuffer.getExternalData().getWorld().getEntityStore().getStore();
|
||||
WorldTimeResource worldTimeResource = store.getResource(WorldTimeResource.getResourceType());
|
||||
WeatherResource weatherResource = store.getResource(WeatherResource.getResourceType());
|
||||
int environment = blockChunk.getEnvironment(x, y, z);
|
||||
int weatherId;
|
||||
if (weatherResource.getForcedWeatherIndex() != 0) {
|
||||
weatherId = weatherResource.getForcedWeatherIndex();
|
||||
} else {
|
||||
weatherId = weatherResource.getWeatherIndexForEnvironment(environment);
|
||||
}
|
||||
|
||||
if (this.weatherIds.contains(weatherId)) {
|
||||
boolean unobstructed = true;
|
||||
|
||||
for (int searchY = y + 1; searchY < 320; searchY++) {
|
||||
int block = blockChunk.getBlock(x, searchY, z);
|
||||
if (block != 0 && block != cropId) {
|
||||
unobstructed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (unobstructed) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSoilWaterExpiring(WorldTimeResource worldTimeResource, TilledSoilBlock soilBlock) {
|
||||
Instant until = soilBlock.getWateredUntil();
|
||||
if (until == null) {
|
||||
return false;
|
||||
} else {
|
||||
Instant now = worldTimeResource.getGameTime();
|
||||
if (now.isAfter(until)) {
|
||||
soilBlock.setWateredUntil(null);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WaterGrowthModifierAsset{blocks="
|
||||
+ Arrays.toString((Object[])this.fluids)
|
||||
+ ", blockIds="
|
||||
+ this.fluidIds
|
||||
+ ", weathers="
|
||||
+ Arrays.toString((Object[])this.weathers)
|
||||
+ ", weatherIds="
|
||||
+ this.weatherIds
|
||||
+ ", rainDuration="
|
||||
+ this.rainDuration
|
||||
+ "} "
|
||||
+ super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.config.stages;
|
||||
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.component.ComponentAccessor;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingStageData;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockStateFarmingStageData extends FarmingStageData {
|
||||
@Nonnull
|
||||
public static BuilderCodec<BlockStateFarmingStageData> CODEC = BuilderCodec.builder(
|
||||
BlockStateFarmingStageData.class, BlockStateFarmingStageData::new, FarmingStageData.BASE_CODEC
|
||||
)
|
||||
.append(new KeyedCodec<>("State", Codec.STRING), (stage, block) -> stage.state = block, stage -> stage.state)
|
||||
.add()
|
||||
.build();
|
||||
protected String state;
|
||||
|
||||
public BlockStateFarmingStageData() {
|
||||
}
|
||||
|
||||
public String getState() {
|
||||
return this.state;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(
|
||||
ComponentAccessor<ChunkStore> commandBuffer,
|
||||
Ref<ChunkStore> sectionRef,
|
||||
Ref<ChunkStore> blockRef,
|
||||
int x,
|
||||
int y,
|
||||
int z,
|
||||
@Nullable FarmingStageData previousStage
|
||||
) {
|
||||
super.apply(commandBuffer, sectionRef, blockRef, x, y, z, previousStage);
|
||||
ChunkSection section = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
|
||||
WorldChunk worldChunk = commandBuffer.getComponent(section.getChunkColumnReference(), WorldChunk.getComponentType());
|
||||
int origBlockId = worldChunk.getBlock(x, y, z);
|
||||
BlockType origBlockType = BlockType.getAssetMap().getAsset(origBlockId);
|
||||
BlockType blockType = origBlockType.getBlockForState(this.state);
|
||||
if (blockType != null) {
|
||||
int newType = BlockType.getAssetMap().getIndex(blockType.getId());
|
||||
if (origBlockId != newType) {
|
||||
int rotation = worldChunk.getRotationIndex(x, y, z);
|
||||
commandBuffer.getExternalData().getWorld().execute(() -> worldChunk.setBlock(x, y, z, newType, blockType, rotation, 0, 2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BlockStateFarmingStageData{state='" + this.state + "'} " + super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.config.stages;
|
||||
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.component.ComponentAccessor;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingStageData;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class BlockTypeFarmingStageData extends FarmingStageData {
|
||||
@Nonnull
|
||||
public static BuilderCodec<BlockTypeFarmingStageData> CODEC = BuilderCodec.builder(
|
||||
BlockTypeFarmingStageData.class, BlockTypeFarmingStageData::new, FarmingStageData.BASE_CODEC
|
||||
)
|
||||
.append(new KeyedCodec<>("Block", Codec.STRING), (stage, block) -> stage.block = block, stage -> stage.block)
|
||||
.addValidatorLate(() -> BlockType.VALIDATOR_CACHE.getValidator().late())
|
||||
.add()
|
||||
.build();
|
||||
protected String block;
|
||||
|
||||
public BlockTypeFarmingStageData() {
|
||||
}
|
||||
|
||||
public String getBlock() {
|
||||
return this.block;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(
|
||||
ComponentAccessor<ChunkStore> commandBuffer,
|
||||
Ref<ChunkStore> sectionRef,
|
||||
Ref<ChunkStore> blockRef,
|
||||
int x,
|
||||
int y,
|
||||
int z,
|
||||
@Nullable FarmingStageData previousStage
|
||||
) {
|
||||
super.apply(commandBuffer, sectionRef, blockRef, x, y, z, previousStage);
|
||||
ChunkSection section = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
|
||||
WorldChunk worldChunk = commandBuffer.getComponent(section.getChunkColumnReference(), WorldChunk.getComponentType());
|
||||
int blockId = BlockType.getAssetMap().getIndex(this.block);
|
||||
if (blockId != worldChunk.getBlock(x, y, z)) {
|
||||
BlockType blockType = BlockType.getAssetMap().getAsset(blockId);
|
||||
commandBuffer.getExternalData().getWorld().execute(() -> worldChunk.setBlock(x, y, z, blockId, blockType, worldChunk.getRotationIndex(x, y, z), 0, 2));
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BlockTypeFarmingStageData{block=" + this.block + "} " + super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.config.stages;
|
||||
|
||||
import com.hypixel.hytale.assetstore.AssetPack;
|
||||
import com.hypixel.hytale.assetstore.AssetRegistry;
|
||||
import com.hypixel.hytale.assetstore.map.BlockTypeAssetMap;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.common.map.IWeightedElement;
|
||||
import com.hypixel.hytale.common.map.IWeightedMap;
|
||||
import com.hypixel.hytale.component.ComponentAccessor;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
import com.hypixel.hytale.math.util.FastRandom;
|
||||
import com.hypixel.hytale.math.util.HashUtil;
|
||||
import com.hypixel.hytale.math.util.MathUtil;
|
||||
import com.hypixel.hytale.math.vector.Vector3i;
|
||||
import com.hypixel.hytale.protocol.BlockMaterial;
|
||||
import com.hypixel.hytale.server.core.asset.AssetModule;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.RotationTuple;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingStageData;
|
||||
import com.hypixel.hytale.server.core.codec.WeightedMapCodec;
|
||||
import com.hypixel.hytale.server.core.prefab.PrefabRotation;
|
||||
import com.hypixel.hytale.server.core.prefab.PrefabStore;
|
||||
import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferCall;
|
||||
import com.hypixel.hytale.server.core.prefab.selection.buffer.PrefabBufferUtil;
|
||||
import com.hypixel.hytale.server.core.prefab.selection.buffer.impl.IPrefabBuffer;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.accessor.LocalCachedChunkAccessor;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import com.hypixel.hytale.server.core.util.PrefabUtil;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class PrefabFarmingStageData extends FarmingStageData {
|
||||
public static final float MIN_VOLUME_PREFAB = 125.0F;
|
||||
public static final float MAX_VOLUME_PREFAB = 1000.0F;
|
||||
public static final float MIN_BROKEN_PARTICLE_RATE = 0.25F;
|
||||
public static final float MAX_BROKEN_PARTICLE_RATE = 0.75F;
|
||||
private static final String[] EMPTY_REPLACE_MASK = new String[0];
|
||||
@Nonnull
|
||||
public static BuilderCodec<PrefabFarmingStageData> CODEC = BuilderCodec.builder(
|
||||
PrefabFarmingStageData.class, PrefabFarmingStageData::new, FarmingStageData.BASE_CODEC
|
||||
)
|
||||
.append(
|
||||
new KeyedCodec<>("Prefabs", new WeightedMapCodec<>(PrefabFarmingStageData.PrefabStage.CODEC, PrefabFarmingStageData.PrefabStage.EMPTY_ARRAY)),
|
||||
(stage, prefabStages) -> stage.prefabStages = prefabStages,
|
||||
stage -> stage.prefabStages
|
||||
)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("ReplaceMaskTags", new ArrayCodec<>(Codec.STRING, String[]::new)),
|
||||
(stage, replaceMask) -> stage.replaceMaskTags = replaceMask,
|
||||
stage -> stage.replaceMaskTags
|
||||
)
|
||||
.add()
|
||||
.afterDecode(PrefabFarmingStageData::processConfig)
|
||||
.build();
|
||||
protected IWeightedMap<PrefabFarmingStageData.PrefabStage> prefabStages;
|
||||
private String[] replaceMaskTags = EMPTY_REPLACE_MASK;
|
||||
private int[] replaceMaskTagIndices;
|
||||
|
||||
public PrefabFarmingStageData() {
|
||||
}
|
||||
|
||||
private static double computeParticlesRate(@Nonnull IPrefabBuffer prefab) {
|
||||
double xLength = prefab.getMaxX() - prefab.getMinX();
|
||||
double yLength = prefab.getMaxY() - prefab.getMinY();
|
||||
double zLength = prefab.getMaxZ() - prefab.getMinZ();
|
||||
double volume = xLength * yLength * zLength;
|
||||
double ratio = -5.7142857E-4F;
|
||||
double rate = (volume - 125.0) * ratio;
|
||||
return MathUtil.clamp(rate + 0.75, 0.25, 0.75);
|
||||
}
|
||||
|
||||
private static boolean isPrefabBlockIntact(
|
||||
LocalCachedChunkAccessor chunkAccessor,
|
||||
int worldX,
|
||||
int worldY,
|
||||
int worldZ,
|
||||
int blockX,
|
||||
int blockY,
|
||||
int blockZ,
|
||||
int blockId,
|
||||
int rotation,
|
||||
PrefabRotation prefabRotation
|
||||
) {
|
||||
int globalX = prefabRotation.getX(blockX, blockZ) + worldX;
|
||||
int globalY = blockY + worldY;
|
||||
int globalZ = prefabRotation.getZ(blockX, blockZ) + worldZ;
|
||||
BlockType block = BlockType.getAssetMap().getAsset(blockId);
|
||||
if (block.getMaterial() == BlockMaterial.Empty) {
|
||||
return true;
|
||||
} else {
|
||||
WorldChunk chunk = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(globalX, globalZ));
|
||||
if (chunk == null) {
|
||||
return false;
|
||||
} else {
|
||||
int worldBlockId = chunk.getBlock(globalX, globalY, globalZ);
|
||||
if (worldBlockId != blockId) {
|
||||
return false;
|
||||
} else {
|
||||
int expectedRotation = prefabRotation.getRotation(rotation);
|
||||
int worldRotation = chunk.getRotationIndex(globalX, globalY, globalZ);
|
||||
return worldRotation == expectedRotation;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isPrefabIntact(
|
||||
IPrefabBuffer prefabBuffer, LocalCachedChunkAccessor chunkAccessor, int worldX, int worldY, int worldZ, PrefabRotation prefabRotation, FastRandom random
|
||||
) {
|
||||
return prefabBuffer.forEachRaw(
|
||||
IPrefabBuffer.iterateAllColumns(),
|
||||
(blockX, blockY, blockZ, blockId, chance, holder, supportValue, rotation, filler, t) -> isPrefabBlockIntact(
|
||||
chunkAccessor, worldX, worldY, worldZ, blockX, blockY, blockZ, blockId, rotation, prefabRotation
|
||||
),
|
||||
(fluidX, fluidY, fluidZ, fluidId, level, o) -> true,
|
||||
null,
|
||||
new PrefabBufferCall(random, prefabRotation)
|
||||
);
|
||||
}
|
||||
|
||||
public IWeightedMap<PrefabFarmingStageData.PrefabStage> getPrefabStages() {
|
||||
return this.prefabStages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(
|
||||
ComponentAccessor<ChunkStore> commandBuffer,
|
||||
Ref<ChunkStore> sectionRef,
|
||||
Ref<ChunkStore> blockRef,
|
||||
int x,
|
||||
int y,
|
||||
int z,
|
||||
@Nullable FarmingStageData previousStage
|
||||
) {
|
||||
FarmingBlock farming = commandBuffer.getComponent(blockRef, FarmingBlock.getComponentType());
|
||||
IPrefabBuffer prefabBuffer = this.getCachedPrefab(x, y, z, farming.getGeneration());
|
||||
BlockSection blockSection = commandBuffer.getComponent(sectionRef, BlockSection.getComponentType());
|
||||
int randomRotation = HashUtil.randomInt(x, y, z, Rotation.VALUES.length);
|
||||
RotationTuple yaw = RotationTuple.of(Rotation.VALUES[randomRotation], Rotation.None);
|
||||
World world = commandBuffer.getExternalData().getWorld();
|
||||
ChunkSection section = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
|
||||
int worldX = ChunkUtil.worldCoordFromLocalCoord(section.getX(), x);
|
||||
int worldY = ChunkUtil.worldCoordFromLocalCoord(section.getY(), y);
|
||||
int worldZ = ChunkUtil.worldCoordFromLocalCoord(section.getZ(), z);
|
||||
if (farming.getPreviousBlockType() == null) {
|
||||
farming.setPreviousBlockType(BlockType.getAssetMap().getAsset(blockSection.get(x, y, z)).getId());
|
||||
}
|
||||
|
||||
double xLength = prefabBuffer.getMaxX() - prefabBuffer.getMinX();
|
||||
double zLength = prefabBuffer.getMaxZ() - prefabBuffer.getMinZ();
|
||||
int prefabRadius = (int)MathUtil.fastFloor(0.5 * Math.sqrt(xLength * xLength + zLength * zLength));
|
||||
LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, x, z, prefabRadius);
|
||||
FastRandom random = new FastRandom();
|
||||
PrefabRotation prefabRotation = PrefabRotation.fromRotation(yaw.yaw());
|
||||
BlockTypeAssetMap<String, BlockType> blockTypeMap = BlockType.getAssetMap();
|
||||
if (previousStage instanceof PrefabFarmingStageData oldPrefab) {
|
||||
IPrefabBuffer oldPrefabBuffer = oldPrefab.getCachedPrefab(worldX, worldY, worldZ, farming.getGeneration() - 1);
|
||||
double brokenParticlesRate = computeParticlesRate(prefabBuffer);
|
||||
world.execute(
|
||||
() -> {
|
||||
boolean isIntact = isPrefabIntact(oldPrefabBuffer, chunkAccessor, worldX, worldY, worldZ, prefabRotation, random);
|
||||
if (isIntact) {
|
||||
boolean isUnobstructed = prefabBuffer.compare(
|
||||
(px, py, pz, blockId, stateWrapper, chance, rotation, filler, secondBlockId, secondStateWrapper, secondChance, secondRotation, secondFiller, prefabBufferCall) -> {
|
||||
int bx = worldX + px;
|
||||
int by = worldY + py;
|
||||
int bz = worldZ + pz;
|
||||
if ((secondBlockId == 0 || secondBlockId == Integer.MIN_VALUE) && blockId != 0 && blockId != Integer.MIN_VALUE) {
|
||||
WorldChunk nonTickingChunk = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(bx, bz));
|
||||
int worldBlock = nonTickingChunk.getBlock(bx, by, bz);
|
||||
return !this.doesBlockObstruct(blockId, worldBlock);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
new PrefabBufferCall(random, prefabRotation),
|
||||
oldPrefabBuffer
|
||||
);
|
||||
if (isUnobstructed) {
|
||||
prefabBuffer.compare(
|
||||
(px, py, pz, blockId, stateWrapper, chance, rotation, filler, secondBlockId, secondStateWrapper, secondChance, secondRotation, secondFiller, prefabBufferCall) -> {
|
||||
int bx = worldX + px;
|
||||
int by = worldY + py;
|
||||
int bz = worldZ + pz;
|
||||
WorldChunk nonTickingChunk = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(bx, bz));
|
||||
int updatedSetBlockSettings = 2;
|
||||
if (random.nextDouble() > brokenParticlesRate) {
|
||||
updatedSetBlockSettings |= 4;
|
||||
}
|
||||
|
||||
if (blockId != 0 && blockId != Integer.MIN_VALUE) {
|
||||
BlockType block = blockTypeMap.getAsset(blockId);
|
||||
if (filler != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int worldBlock = nonTickingChunk.getBlock(bx, by, bz);
|
||||
if ((secondBlockId == 0 || secondBlockId == Integer.MIN_VALUE) && !this.canReplace(worldBlock, blockTypeMap)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nonTickingChunk.setBlock(bx, by, bz, blockId, block, rotation, filler, updatedSetBlockSettings);
|
||||
if (stateWrapper != null) {
|
||||
nonTickingChunk.setState(bx, by, bz, stateWrapper.clone());
|
||||
}
|
||||
} else if (secondBlockId != 0 && secondBlockId != Integer.MIN_VALUE) {
|
||||
nonTickingChunk.breakBlock(bx, by, bz, updatedSetBlockSettings);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
new PrefabBufferCall(random, prefabRotation),
|
||||
oldPrefabBuffer
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
super.apply(commandBuffer, sectionRef, blockRef, x, y, z, previousStage);
|
||||
world.execute(
|
||||
() -> {
|
||||
boolean isUnObstructed = prefabBuffer.forEachRaw(
|
||||
IPrefabBuffer.iterateAllColumns(), (blockX, blockY, blockZ, blockId, chance, holder, supportValue, rotation, filler, t) -> {
|
||||
int bx = worldX + prefabRotation.getX(blockX, blockZ);
|
||||
int by = worldY + blockY;
|
||||
int bz = worldZ + prefabRotation.getX(blockZ, blockX);
|
||||
if (blockId != 0 && blockId != Integer.MIN_VALUE) {
|
||||
int worldBlock = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(bx, bz)).getBlock(bx, by, bz);
|
||||
return !this.doesBlockObstruct(blockId, worldBlock);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}, (fluidX, fluidY, fluidZ, fluidId, level, o) -> true, null, new PrefabBufferCall(random, prefabRotation)
|
||||
);
|
||||
if (isUnObstructed) {
|
||||
prefabBuffer.forEach(
|
||||
IPrefabBuffer.iterateAllColumns(), (blockX, blockY, blockZ, blockId, holder, supportValue, rotation, filler, t, fluidId, fluidLevel) -> {
|
||||
int bx = worldX + blockX;
|
||||
int by = worldY + blockY;
|
||||
int bz = worldZ + blockZ;
|
||||
WorldChunk nonTickingChunk = chunkAccessor.getNonTickingChunk(ChunkUtil.indexChunkFromBlock(bx, bz));
|
||||
int updatedSetBlockSettings = 2;
|
||||
if (blockId != 0 && blockId != Integer.MIN_VALUE) {
|
||||
BlockType block = blockTypeMap.getAsset(blockId);
|
||||
if (filler != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int worldBlock = nonTickingChunk.getBlock(bx, by, bz);
|
||||
if (!this.canReplace(worldBlock, blockTypeMap)) {
|
||||
return;
|
||||
}
|
||||
|
||||
nonTickingChunk.setBlock(bx, by, bz, blockId, block, rotation, filler, updatedSetBlockSettings);
|
||||
if (holder != null) {
|
||||
nonTickingChunk.setState(bx, by, bz, holder.clone());
|
||||
}
|
||||
}
|
||||
}, null, null, new PrefabBufferCall(random, prefabRotation)
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean doesBlockObstruct(int blockId, int worldBlockId) {
|
||||
BlockTypeAssetMap<String, BlockType> assetMap = BlockType.getAssetMap();
|
||||
BlockType blockType = assetMap.getAsset(blockId);
|
||||
return blockType != null && blockType.getMaterial() != BlockMaterial.Empty ? !this.canReplace(worldBlockId, assetMap) : false;
|
||||
}
|
||||
|
||||
private boolean canReplace(int worldBlockId, BlockTypeAssetMap<String, BlockType> assetMap) {
|
||||
BlockType worldBlockType = assetMap.getAsset(worldBlockId);
|
||||
if (worldBlockType != null && worldBlockType.getMaterial() != BlockMaterial.Empty) {
|
||||
for (int tagIndex : this.replaceMaskTagIndices) {
|
||||
if (assetMap.getIndexesForTag(tagIndex).contains(worldBlockId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(ComponentAccessor<ChunkStore> commandBuffer, Ref<ChunkStore> sectionRef, Ref<ChunkStore> blockRef, int x, int y, int z) {
|
||||
super.remove(commandBuffer, sectionRef, blockRef, x, y, z);
|
||||
ChunkSection section = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
|
||||
int worldX = ChunkUtil.worldCoordFromLocalCoord(section.getX(), x);
|
||||
int worldY = ChunkUtil.worldCoordFromLocalCoord(section.getY(), y);
|
||||
int worldZ = ChunkUtil.worldCoordFromLocalCoord(section.getZ(), z);
|
||||
FarmingBlock farming = commandBuffer.getComponent(blockRef, FarmingBlock.getComponentType());
|
||||
IPrefabBuffer prefab = this.getCachedPrefab(worldX, worldY, worldZ, farming.getGeneration() - 1);
|
||||
RotationTuple rotation = commandBuffer.getComponent(sectionRef, BlockSection.getComponentType()).getRotation(x, y, z);
|
||||
double rate = computeParticlesRate(prefab);
|
||||
World world = commandBuffer.getExternalData().getWorld();
|
||||
world.execute(() -> PrefabUtil.remove(prefab, world, new Vector3i(worldX, worldY, worldZ), rotation.yaw(), true, new FastRandom(), 2, rate));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private IPrefabBuffer getCachedPrefab(int x, int y, int z, int generation) {
|
||||
return PrefabBufferUtil.getCached(this.prefabStages.get(HashUtil.random(x, y, z, generation)).getResolvedPath());
|
||||
}
|
||||
|
||||
private void processConfig() {
|
||||
this.replaceMaskTagIndices = new int[this.replaceMaskTags.length];
|
||||
|
||||
for (int i = 0; i < this.replaceMaskTags.length; i++) {
|
||||
this.replaceMaskTagIndices[i] = AssetRegistry.getOrCreateTagIndex(this.replaceMaskTags[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PrefabFarmingStageData{replaceMaskTags=" + Arrays.toString((Object[])this.replaceMaskTags) + ", prefabStages=" + this.prefabStages + "}";
|
||||
}
|
||||
|
||||
public static class PrefabStage implements IWeightedElement {
|
||||
public static final PrefabFarmingStageData.PrefabStage[] EMPTY_ARRAY = new PrefabFarmingStageData.PrefabStage[0];
|
||||
@Nonnull
|
||||
public static Codec<PrefabFarmingStageData.PrefabStage> CODEC = BuilderCodec.builder(
|
||||
PrefabFarmingStageData.PrefabStage.class, PrefabFarmingStageData.PrefabStage::new
|
||||
)
|
||||
.append(new KeyedCodec<>("Weight", Codec.INTEGER), (prefabStage, integer) -> prefabStage.weight = integer, prefabStage -> prefabStage.weight)
|
||||
.addValidator(Validators.greaterThanOrEqual(1))
|
||||
.add()
|
||||
.<String>append(new KeyedCodec<>("Path", Codec.STRING), (prefabStage, s) -> prefabStage.path = s, prefabStage -> prefabStage.path)
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.build();
|
||||
protected int weight = 1;
|
||||
protected String path;
|
||||
|
||||
public PrefabStage() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getWeight() {
|
||||
return this.weight;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Path getResolvedPath() {
|
||||
for (AssetPack pack : AssetModule.get().getAssetPacks()) {
|
||||
Path assetPath = pack.getRoot().resolve("Server").resolve("Prefabs").resolve(this.path);
|
||||
if (Files.exists(assetPath)) {
|
||||
return assetPath;
|
||||
}
|
||||
}
|
||||
|
||||
return PrefabStore.get().getAssetPrefabsPath().resolve(this.path);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PrefabStage{weight=" + this.weight + ", path='" + this.path + "'}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,273 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.config.stages.spread;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock;
|
||||
import com.hypixel.hytale.builtin.blockphysics.BlockPhysicsSystems;
|
||||
import com.hypixel.hytale.builtin.blockphysics.BlockPhysicsUtil;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.EnumCodec;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.common.map.IWeightedElement;
|
||||
import com.hypixel.hytale.common.map.IWeightedMap;
|
||||
import com.hypixel.hytale.component.ComponentAccessor;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.math.range.IntRange;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
import com.hypixel.hytale.math.util.FastRandom;
|
||||
import com.hypixel.hytale.math.util.MathUtil;
|
||||
import com.hypixel.hytale.math.util.TrigMathUtil;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.Rotation;
|
||||
import com.hypixel.hytale.server.core.blocktype.component.BlockPhysics;
|
||||
import com.hypixel.hytale.server.core.codec.WeightedMapCodec;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.accessor.LocalCachedChunkAccessor;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.FluidSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class DirectionalGrowthBehaviour extends SpreadGrowthBehaviour {
|
||||
public static final BuilderCodec<DirectionalGrowthBehaviour> CODEC = BuilderCodec.builder(
|
||||
DirectionalGrowthBehaviour.class, DirectionalGrowthBehaviour::new, BASE_CODEC
|
||||
)
|
||||
.append(
|
||||
new KeyedCodec<>(
|
||||
"GrowthBlockTypes", new WeightedMapCodec<>(DirectionalGrowthBehaviour.BlockTypeWeight.CODEC, new DirectionalGrowthBehaviour.BlockTypeWeight[0])
|
||||
),
|
||||
(directionalGrowthBehaviour, blockTypeWeightIWeightedMap) -> directionalGrowthBehaviour.blockTypes = blockTypeWeightIWeightedMap,
|
||||
directionalGrowthBehaviour -> directionalGrowthBehaviour.blockTypes
|
||||
)
|
||||
.documentation("Defines a map of the possible BlockType to spread.")
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.<IntRange>append(
|
||||
new KeyedCodec<>("Horizontal", IntRange.CODEC),
|
||||
(directionalGrowthBehaviour, intRange) -> directionalGrowthBehaviour.horizontalRange = intRange,
|
||||
directionalGrowthBehaviour -> directionalGrowthBehaviour.horizontalRange
|
||||
)
|
||||
.documentation("Defines if the spread can happen horizontally. The range must be set with positive integers.")
|
||||
.add()
|
||||
.<IntRange>append(
|
||||
new KeyedCodec<>("Vertical", IntRange.CODEC),
|
||||
(directionalGrowthBehaviour, intRange) -> directionalGrowthBehaviour.verticalRange = intRange,
|
||||
directionalGrowthBehaviour -> directionalGrowthBehaviour.verticalRange
|
||||
)
|
||||
.documentation("Defines if the spread can happen vertically. The range must be set with positive integers.")
|
||||
.add()
|
||||
.<DirectionalGrowthBehaviour.VerticalDirection>append(
|
||||
new KeyedCodec<>("VerticalDirection", new EnumCodec<>(DirectionalGrowthBehaviour.VerticalDirection.class)),
|
||||
(directionalGrowthBehaviour, verticalDirection) -> directionalGrowthBehaviour.verticalDirection = verticalDirection,
|
||||
directionalGrowthBehaviour -> directionalGrowthBehaviour.verticalDirection
|
||||
)
|
||||
.documentation("Defines in which direction the vertical spread should happen. Possible values are: 'Upwards' and 'Downwards', default value: 'Upwards'.")
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.build();
|
||||
private static final int PLACE_BLOCK_TRIES = 100;
|
||||
protected IWeightedMap<DirectionalGrowthBehaviour.BlockTypeWeight> blockTypes;
|
||||
protected IntRange horizontalRange;
|
||||
protected IntRange verticalRange;
|
||||
protected DirectionalGrowthBehaviour.VerticalDirection verticalDirection = DirectionalGrowthBehaviour.VerticalDirection.BOTH;
|
||||
|
||||
public DirectionalGrowthBehaviour() {
|
||||
}
|
||||
|
||||
public IWeightedMap<DirectionalGrowthBehaviour.BlockTypeWeight> getBlockTypes() {
|
||||
return this.blockTypes;
|
||||
}
|
||||
|
||||
public IntRange getHorizontalRange() {
|
||||
return this.horizontalRange;
|
||||
}
|
||||
|
||||
public IntRange getVerticalRange() {
|
||||
return this.verticalRange;
|
||||
}
|
||||
|
||||
public DirectionalGrowthBehaviour.VerticalDirection getVerticalDirection() {
|
||||
return this.verticalDirection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(
|
||||
ComponentAccessor<ChunkStore> commandBuffer,
|
||||
Ref<ChunkStore> sectionRef,
|
||||
Ref<ChunkStore> blockRef,
|
||||
int worldX,
|
||||
int worldY,
|
||||
int worldZ,
|
||||
float newSpreadRate
|
||||
) {
|
||||
int x = 0;
|
||||
int z = 0;
|
||||
FastRandom random = new FastRandom();
|
||||
String blockTypeKey = this.blockTypes.get(random).getBlockTypeKey();
|
||||
World world = commandBuffer.getExternalData().getWorld();
|
||||
LocalCachedChunkAccessor chunkAccessor = LocalCachedChunkAccessor.atWorldCoords(world, worldX, worldZ, 1);
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
if (this.horizontalRange != null) {
|
||||
double angle = (float) (Math.PI * 2) * random.nextFloat();
|
||||
int radius = this.horizontalRange.getInt(random.nextFloat());
|
||||
x = MathUtil.fastRound(radius * TrigMathUtil.cos(angle));
|
||||
z = MathUtil.fastRound(radius * TrigMathUtil.sin(angle));
|
||||
}
|
||||
|
||||
int targetX = worldX + x;
|
||||
int targetZ = worldZ + z;
|
||||
int chunkX = ChunkUtil.chunkCoordinate(targetX);
|
||||
int chunkZ = ChunkUtil.chunkCoordinate(targetZ);
|
||||
WorldChunk chunk = chunkAccessor.getChunkIfInMemory(ChunkUtil.indexChunk(chunkX, chunkZ));
|
||||
if (chunk != null) {
|
||||
int targetY;
|
||||
if (this.verticalRange != null) {
|
||||
int directionValue = switch (this.verticalDirection) {
|
||||
case DOWNWARDS, UPWARDS -> this.verticalDirection.getValue();
|
||||
case BOTH -> random.nextBoolean() ? 1 : -1;
|
||||
};
|
||||
targetY = worldY + this.verticalRange.getInt(random.nextFloat()) * directionValue;
|
||||
} else {
|
||||
targetY = chunk.getHeight(targetX, targetZ) + 1;
|
||||
}
|
||||
|
||||
if (this.tryPlaceBlock(world, chunk, targetX, targetY, targetZ, blockTypeKey, 0)) {
|
||||
int finalTargetY = targetY;
|
||||
world.execute(() -> {
|
||||
WorldChunk loadedChunk = chunkAccessor.getChunk(ChunkUtil.indexChunk(chunkX, chunkZ));
|
||||
if (loadedChunk != null) {
|
||||
loadedChunk.placeBlock(targetX, finalTargetY, targetZ, blockTypeKey, Rotation.None, Rotation.None, Rotation.None);
|
||||
decaySpread(commandBuffer, loadedChunk.getBlockComponentChunk(), targetX, finalTargetY, targetZ, newSpreadRate);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void decaySpread(
|
||||
ComponentAccessor<ChunkStore> commandBuffer, BlockComponentChunk blockComponentChunk, int worldX, int worldY, int worldZ, float newSpreadRate
|
||||
) {
|
||||
Ref<ChunkStore> blockRefPlaced = blockComponentChunk.getEntityReference(ChunkUtil.indexBlockInColumn(worldX, worldY, worldZ));
|
||||
if (blockRefPlaced != null) {
|
||||
FarmingBlock farmingPlaced = commandBuffer.getComponent(blockRefPlaced, FarmingBlock.getComponentType());
|
||||
if (farmingPlaced != null) {
|
||||
farmingPlaced.setSpreadRate(newSpreadRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean tryPlaceBlock(@Nonnull World world, @Nonnull WorldChunk chunk, int worldX, int worldY, int worldZ, String blockTypeKey, int rotation) {
|
||||
if (chunk.getBlock(worldX, worldY, worldZ) != 0) {
|
||||
return false;
|
||||
} else if (!this.validatePosition(world, worldX, worldY, worldZ)) {
|
||||
return false;
|
||||
} else {
|
||||
BlockType blockType = BlockType.getAssetMap().getAsset(blockTypeKey);
|
||||
if (blockType == null) {
|
||||
return false;
|
||||
} else if (!chunk.testPlaceBlock(worldX, worldY, worldZ, blockType, rotation)) {
|
||||
return false;
|
||||
} else {
|
||||
int cx = chunk.getX();
|
||||
int cz = chunk.getZ();
|
||||
int cy = ChunkUtil.indexSection(worldY);
|
||||
Ref<ChunkStore> sectionRef = world.getChunkStore().getChunkSectionReference(cx, cy, cz);
|
||||
if (sectionRef == null) {
|
||||
return false;
|
||||
} else {
|
||||
Store<ChunkStore> store = world.getChunkStore().getStore();
|
||||
BlockPhysics blockPhysics = store.getComponent(sectionRef, BlockPhysics.getComponentType());
|
||||
FluidSection fluidSection = store.getComponent(sectionRef, FluidSection.getComponentType());
|
||||
BlockSection blockSection = store.getComponent(sectionRef, BlockSection.getComponentType());
|
||||
int filler = blockSection.getFiller(worldX, worldY, worldZ);
|
||||
BlockPhysicsSystems.CachedAccessor cachedAccessor = BlockPhysicsSystems.CachedAccessor.of(
|
||||
store, blockSection, blockPhysics, fluidSection, cx, cy, cz, 14
|
||||
);
|
||||
return BlockPhysicsUtil.testBlockPhysics(
|
||||
cachedAccessor, blockSection, blockPhysics, fluidSection, worldX, worldY, worldZ, blockType, rotation, filler
|
||||
)
|
||||
!= 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "DirectionalGrowthBehaviour{blockTypes="
|
||||
+ this.blockTypes
|
||||
+ ", horizontalRange="
|
||||
+ this.horizontalRange
|
||||
+ ", verticalRange="
|
||||
+ this.verticalRange
|
||||
+ ", verticalDirection="
|
||||
+ this.verticalDirection
|
||||
+ "} "
|
||||
+ super.toString();
|
||||
}
|
||||
|
||||
public static class BlockTypeWeight implements IWeightedElement {
|
||||
@Nonnull
|
||||
public static BuilderCodec<DirectionalGrowthBehaviour.BlockTypeWeight> CODEC = BuilderCodec.builder(
|
||||
DirectionalGrowthBehaviour.BlockTypeWeight.class, DirectionalGrowthBehaviour.BlockTypeWeight::new
|
||||
)
|
||||
.append(
|
||||
new KeyedCodec<>("Weight", Codec.DOUBLE), (blockTypeWeight, integer) -> blockTypeWeight.weight = integer, blockTypeWeight -> blockTypeWeight.weight
|
||||
)
|
||||
.documentation("Defines the probability to have this entry.")
|
||||
.addValidator(Validators.greaterThan(0.0))
|
||||
.add()
|
||||
.<String>append(
|
||||
new KeyedCodec<>("BlockType", Codec.STRING),
|
||||
(blockTypeWeight, blockTypeKey) -> blockTypeWeight.blockTypeKey = blockTypeKey,
|
||||
blockTypeWeight -> blockTypeWeight.blockTypeKey
|
||||
)
|
||||
.documentation("Defines the BlockType that'll be spread")
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.build();
|
||||
protected double weight = 1.0;
|
||||
protected String blockTypeKey;
|
||||
|
||||
public BlockTypeWeight() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getWeight() {
|
||||
return this.weight;
|
||||
}
|
||||
|
||||
public String getBlockTypeKey() {
|
||||
return this.blockTypeKey;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BlockTypeWeight{weight=" + this.weight + ", blockTypeKey=" + this.blockTypeKey + "}";
|
||||
}
|
||||
}
|
||||
|
||||
private static enum VerticalDirection {
|
||||
DOWNWARDS(-1),
|
||||
BOTH(0),
|
||||
UPWARDS(1);
|
||||
|
||||
private final int value;
|
||||
|
||||
private VerticalDirection(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.config.stages.spread;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.component.ComponentAccessor;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.math.range.IntRange;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
import com.hypixel.hytale.math.util.HashUtil;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingStageData;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.ChunkSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class SpreadFarmingStageData extends FarmingStageData {
|
||||
@Nonnull
|
||||
public static BuilderCodec<SpreadFarmingStageData> CODEC = BuilderCodec.builder(
|
||||
SpreadFarmingStageData.class, SpreadFarmingStageData::new, FarmingStageData.BASE_CODEC
|
||||
)
|
||||
.append(
|
||||
new KeyedCodec<>("Executions", IntRange.CODEC),
|
||||
(spreadFarmingStageData, intRange) -> spreadFarmingStageData.executions = intRange,
|
||||
spreadFarmingStageData -> spreadFarmingStageData.executions
|
||||
)
|
||||
.documentation("Defines the number of times the stage will be repeated. Range must be positive, min value must be >= 1.")
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.<IntRange>append(
|
||||
new KeyedCodec<>("SpreadDecayPercent", IntRange.CODEC),
|
||||
(spreadFarmingStageData, intRange) -> spreadFarmingStageData.spreadDecayPercent = intRange,
|
||||
spreadFarmingStageData -> spreadFarmingStageData.spreadDecayPercent
|
||||
)
|
||||
.documentation(
|
||||
"The amount to reduce (linear decay) the spread rate (chance to spread) for any spawned blocks that also have a spread stage. Range must be positive."
|
||||
)
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.<SpreadGrowthBehaviour[]>append(
|
||||
new KeyedCodec<>("GrowthBehaviours", new ArrayCodec<>(SpreadGrowthBehaviour.CODEC, SpreadGrowthBehaviour[]::new)),
|
||||
(spreadFarmingStageData, spreadGrowthBehaviour) -> spreadFarmingStageData.spreadGrowthBehaviours = spreadGrowthBehaviour,
|
||||
spreadFarmingStageData -> spreadFarmingStageData.spreadGrowthBehaviours
|
||||
)
|
||||
.documentation("Defines an array of the different growth behaviours that'll be run for each execution.")
|
||||
.addValidator(Validators.nonEmptyArray())
|
||||
.add()
|
||||
.afterDecode(
|
||||
stageData -> {
|
||||
if (stageData.executions != null && stageData.executions.getInclusiveMin() < 1) {
|
||||
throw new IllegalArgumentException(
|
||||
"The min value for Executions range must be >= 1! Current min value is: " + stageData.executions.getInclusiveMin()
|
||||
);
|
||||
} else if (stageData.spreadDecayPercent != null && stageData.spreadDecayPercent.getInclusiveMin() < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"The min value for SpreadDecayPercent range must be >= 0! Current min value is: " + stageData.spreadDecayPercent.getInclusiveMin()
|
||||
);
|
||||
}
|
||||
}
|
||||
)
|
||||
.build();
|
||||
protected IntRange executions;
|
||||
protected IntRange spreadDecayPercent;
|
||||
protected SpreadGrowthBehaviour[] spreadGrowthBehaviours;
|
||||
|
||||
public SpreadFarmingStageData() {
|
||||
}
|
||||
|
||||
public IntRange getExecutions() {
|
||||
return this.executions;
|
||||
}
|
||||
|
||||
public IntRange getSpreadDecayPercent() {
|
||||
return this.spreadDecayPercent;
|
||||
}
|
||||
|
||||
public SpreadGrowthBehaviour[] getSpreadGrowthBehaviours() {
|
||||
return this.spreadGrowthBehaviours;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean implementsShouldStop() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldStop(ComponentAccessor<ChunkStore> commandBuffer, Ref<ChunkStore> sectionRef, Ref<ChunkStore> blockRef, int x, int y, int z) {
|
||||
FarmingBlock farming = commandBuffer.getComponent(blockRef, FarmingBlock.getComponentType());
|
||||
float spreadRate = farming.getSpreadRate();
|
||||
ChunkSection section = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
|
||||
int worldX = ChunkUtil.worldCoordFromLocalCoord(section.getX(), x);
|
||||
int worldY = ChunkUtil.worldCoordFromLocalCoord(section.getY(), y);
|
||||
int worldZ = ChunkUtil.worldCoordFromLocalCoord(section.getZ(), z);
|
||||
float executions = this.executions.getInt(HashUtil.random(worldX, worldY, worldZ, farming.getGeneration())) * spreadRate;
|
||||
int executed = farming.getExecutions();
|
||||
return spreadRate <= 0.0F || executed >= executions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(
|
||||
ComponentAccessor<ChunkStore> commandBuffer,
|
||||
Ref<ChunkStore> sectionRef,
|
||||
Ref<ChunkStore> blockRef,
|
||||
int x,
|
||||
int y,
|
||||
int z,
|
||||
@Nullable FarmingStageData previousStage
|
||||
) {
|
||||
super.apply(commandBuffer, sectionRef, blockRef, x, y, z, previousStage);
|
||||
FarmingBlock farming = commandBuffer.getComponent(blockRef, FarmingBlock.getComponentType());
|
||||
ChunkSection section = commandBuffer.getComponent(sectionRef, ChunkSection.getComponentType());
|
||||
int worldX = ChunkUtil.worldCoordFromLocalCoord(section.getX(), x);
|
||||
int worldY = ChunkUtil.worldCoordFromLocalCoord(section.getY(), y);
|
||||
int worldZ = ChunkUtil.worldCoordFromLocalCoord(section.getZ(), z);
|
||||
float spreadRate = farming.getSpreadRate();
|
||||
double executions = Math.floor(this.executions.getInt(HashUtil.random(worldX, worldY, worldZ, farming.getGeneration())) * spreadRate);
|
||||
int executed = farming.getExecutions();
|
||||
if (!(spreadRate <= 0.0F) && !(executed >= executions)) {
|
||||
for (int i = 0; i < this.spreadGrowthBehaviours.length; i++) {
|
||||
SpreadGrowthBehaviour spreadGrowthBehaviour = this.spreadGrowthBehaviours[i];
|
||||
float decayRate = this.spreadDecayPercent.getInt(HashUtil.random(i | (long)farming.getGeneration() << 32, worldX, worldY, worldZ)) / 100.0F;
|
||||
spreadGrowthBehaviour.execute(commandBuffer, sectionRef, blockRef, worldX, worldY, worldZ, spreadRate - decayRate);
|
||||
}
|
||||
|
||||
farming.setExecutions(++executed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(ComponentAccessor<ChunkStore> commandBuffer, Ref<ChunkStore> sectionRef, Ref<ChunkStore> blockRef, int x, int y, int z) {
|
||||
super.remove(commandBuffer, sectionRef, blockRef, x, y, z);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SpreadFarmingStageData{executions="
|
||||
+ this.executions
|
||||
+ ", spreadDecayPercent="
|
||||
+ this.spreadDecayPercent
|
||||
+ ", spreadGrowthBehaviours="
|
||||
+ Arrays.toString((Object[])this.spreadGrowthBehaviours)
|
||||
+ "} "
|
||||
+ super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.config.stages.spread;
|
||||
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
|
||||
import com.hypixel.hytale.codec.lookup.CodecMapCodec;
|
||||
import com.hypixel.hytale.component.ComponentAccessor;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import com.hypixel.hytale.server.core.universe.world.worldlocationcondition.WorldLocationCondition;
|
||||
|
||||
public abstract class SpreadGrowthBehaviour {
|
||||
public static final CodecMapCodec<SpreadGrowthBehaviour> CODEC = new CodecMapCodec<>("Type");
|
||||
public static final BuilderCodec<SpreadGrowthBehaviour> BASE_CODEC = BuilderCodec.abstractBuilder(SpreadGrowthBehaviour.class)
|
||||
.append(
|
||||
new KeyedCodec<>("LocationConditions", new ArrayCodec<>(WorldLocationCondition.CODEC, WorldLocationCondition[]::new)),
|
||||
(spreadGrowthBehaviour, worldLocationConditions) -> spreadGrowthBehaviour.worldLocationConditions = worldLocationConditions,
|
||||
spreadGrowthBehaviour -> spreadGrowthBehaviour.worldLocationConditions
|
||||
)
|
||||
.documentation("Defines the possible location conditions a position has to fulfill to be considered as valid.")
|
||||
.add()
|
||||
.build();
|
||||
protected WorldLocationCondition[] worldLocationConditions;
|
||||
|
||||
public SpreadGrowthBehaviour() {
|
||||
}
|
||||
|
||||
public abstract void execute(ComponentAccessor<ChunkStore> var1, Ref<ChunkStore> var2, Ref<ChunkStore> var3, int var4, int var5, int var6, float var7);
|
||||
|
||||
protected boolean validatePosition(World world, int worldX, int worldY, int worldZ) {
|
||||
if (this.worldLocationConditions == null) {
|
||||
return true;
|
||||
} else {
|
||||
for (int i = 0; i < this.worldLocationConditions.length; i++) {
|
||||
if (!this.worldLocationConditions[i].test(world, worldX, worldY, worldZ)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,337 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.interactions;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.component.AddReason;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Holder;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.logger.HytaleLogger;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
import com.hypixel.hytale.math.vector.Vector3i;
|
||||
import com.hypixel.hytale.protocol.InteractionState;
|
||||
import com.hypixel.hytale.protocol.InteractionType;
|
||||
import com.hypixel.hytale.protocol.WaitForDataFrom;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingData;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.farming.FarmingStageData;
|
||||
import com.hypixel.hytale.server.core.entity.InteractionContext;
|
||||
import com.hypixel.hytale.server.core.inventory.ItemStack;
|
||||
import com.hypixel.hytale.server.core.modules.block.BlockModule;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction;
|
||||
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.BlockComponentChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import java.time.Instant;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ChangeFarmingStageInteraction extends SimpleBlockInteraction {
|
||||
private static final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
|
||||
public static final BuilderCodec<ChangeFarmingStageInteraction> CODEC = BuilderCodec.builder(
|
||||
ChangeFarmingStageInteraction.class, ChangeFarmingStageInteraction::new, SimpleBlockInteraction.CODEC
|
||||
)
|
||||
.documentation("Changes the farming stage of the target block.")
|
||||
.<Integer>appendInherited(
|
||||
new KeyedCodec<>("Stage", Codec.INTEGER),
|
||||
(interaction, stage) -> interaction.targetStage = stage,
|
||||
interaction -> interaction.targetStage,
|
||||
(o, p) -> o.targetStage = p.targetStage
|
||||
)
|
||||
.documentation("The stage index to set (0, 1, 2, etc.). Use -1 for the final stage. Ignored if Increase is set.")
|
||||
.add()
|
||||
.<Integer>appendInherited(
|
||||
new KeyedCodec<>("Increase", Codec.INTEGER),
|
||||
(interaction, increase) -> interaction.increaseBy = increase,
|
||||
interaction -> interaction.increaseBy,
|
||||
(o, p) -> o.increaseBy = p.increaseBy
|
||||
)
|
||||
.documentation("Add this amount to the current stage (e.g., 1 = advance one stage, 2 = advance two stages). Takes priority over Decrease and Stage.")
|
||||
.add()
|
||||
.<Integer>appendInherited(
|
||||
new KeyedCodec<>("Decrease", Codec.INTEGER),
|
||||
(interaction, decrease) -> interaction.decreaseBy = decrease,
|
||||
interaction -> interaction.decreaseBy,
|
||||
(o, p) -> o.decreaseBy = p.decreaseBy
|
||||
)
|
||||
.documentation("Subtract this amount from the current stage (e.g., 1 = go back one stage). Takes priority over Stage.")
|
||||
.add()
|
||||
.<String>appendInherited(
|
||||
new KeyedCodec<>("StageSet", Codec.STRING),
|
||||
(interaction, stageSet) -> interaction.targetStageSet = stageSet,
|
||||
interaction -> interaction.targetStageSet,
|
||||
(o, p) -> o.targetStageSet = p.targetStageSet
|
||||
)
|
||||
.documentation("Optional. The stage set to switch to (e.g., 'Default', 'Harvested'). If not provided, uses current stage set.")
|
||||
.add()
|
||||
.build();
|
||||
protected int targetStage = -1;
|
||||
@Nullable
|
||||
protected Integer increaseBy = null;
|
||||
@Nullable
|
||||
protected Integer decreaseBy = null;
|
||||
@Nullable
|
||||
protected String targetStageSet = null;
|
||||
|
||||
public ChangeFarmingStageInteraction() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public WaitForDataFrom getWaitForDataFrom() {
|
||||
return WaitForDataFrom.Server;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void interactWithBlock(
|
||||
@Nonnull World world,
|
||||
@Nonnull CommandBuffer<EntityStore> commandBuffer,
|
||||
@Nonnull InteractionType type,
|
||||
@Nonnull InteractionContext context,
|
||||
@Nullable ItemStack itemInHand,
|
||||
@Nonnull Vector3i targetBlock,
|
||||
@Nonnull CooldownHandler cooldownHandler
|
||||
) {
|
||||
int x = targetBlock.getX();
|
||||
int y = targetBlock.getY();
|
||||
int z = targetBlock.getZ();
|
||||
LOGGER.atInfo()
|
||||
.log(
|
||||
"[ChangeFarmingStage] Starting interaction at pos=(%d, %d, %d), increaseBy=%s, decreaseBy=%s, targetStage=%d, targetStageSet=%s",
|
||||
x,
|
||||
y,
|
||||
z,
|
||||
this.increaseBy,
|
||||
this.decreaseBy,
|
||||
this.targetStage,
|
||||
this.targetStageSet
|
||||
);
|
||||
WorldChunk worldChunk = world.getChunk(ChunkUtil.indexChunkFromBlock(x, z));
|
||||
if (worldChunk == null) {
|
||||
LOGGER.atWarning().log("[ChangeFarmingStage] FAILED: worldChunk is null at pos=(%d, %d, %d)", x, y, z);
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
BlockType blockType = worldChunk.getBlockType(targetBlock);
|
||||
if (blockType == null) {
|
||||
LOGGER.atWarning().log("[ChangeFarmingStage] FAILED: blockType is null at pos=(%d, %d, %d)", x, y, z);
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
LOGGER.atInfo().log("[ChangeFarmingStage] Block type: %s (id=%s)", blockType.getId(), blockType.getClass().getSimpleName());
|
||||
FarmingData farmingConfig = blockType.getFarming();
|
||||
if (farmingConfig != null && farmingConfig.getStages() != null) {
|
||||
LOGGER.atInfo()
|
||||
.log(
|
||||
"[ChangeFarmingStage] Farming config found. StartingStageSet=%s, StageSetAfterHarvest=%s, AvailableStageSets=%s",
|
||||
farmingConfig.getStartingStageSet(),
|
||||
farmingConfig.getStageSetAfterHarvest(),
|
||||
farmingConfig.getStages() != null ? farmingConfig.getStages().keySet() : "null"
|
||||
);
|
||||
Store<ChunkStore> chunkStore = world.getChunkStore().getStore();
|
||||
WorldTimeResource worldTimeResource = world.getEntityStore().getStore().getResource(WorldTimeResource.getResourceType());
|
||||
Instant now = worldTimeResource.getGameTime();
|
||||
Ref<ChunkStore> chunkRef = world.getChunkStore().getChunkReference(ChunkUtil.indexChunkFromBlock(x, z));
|
||||
if (chunkRef == null) {
|
||||
LOGGER.atWarning().log("[ChangeFarmingStage] FAILED: chunkRef is null at pos=(%d, %d, %d)", x, y, z);
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
BlockComponentChunk blockComponentChunk = chunkStore.getComponent(chunkRef, BlockComponentChunk.getComponentType());
|
||||
if (blockComponentChunk == null) {
|
||||
LOGGER.atWarning().log("[ChangeFarmingStage] FAILED: blockComponentChunk is null at pos=(%d, %d, %d)", x, y, z);
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
int blockIndexColumn = ChunkUtil.indexBlockInColumn(x, y, z);
|
||||
Ref<ChunkStore> blockRef = blockComponentChunk.getEntityReference(blockIndexColumn);
|
||||
boolean hadExistingBlockRef = blockRef != null;
|
||||
LOGGER.atInfo().log("[ChangeFarmingStage] Initial blockRef from getEntityReference: %s", hadExistingBlockRef ? "exists" : "null");
|
||||
String initialStageSetLookup = this.targetStageSet != null ? this.targetStageSet : farmingConfig.getStartingStageSet();
|
||||
FarmingStageData[] stages = farmingConfig.getStages().get(initialStageSetLookup);
|
||||
if (stages != null && stages.length != 0) {
|
||||
LOGGER.atInfo().log("[ChangeFarmingStage] Initial stages lookup: stageSet=%s, stageCount=%d", initialStageSetLookup, stages.length);
|
||||
FarmingBlock farmingBlock;
|
||||
if (blockRef == null) {
|
||||
LOGGER.atInfo().log("[ChangeFarmingStage] Creating new block entity (harvest0 pattern)");
|
||||
Holder<ChunkStore> blockEntity = ChunkStore.REGISTRY.newHolder();
|
||||
blockEntity.putComponent(BlockModule.BlockStateInfo.getComponentType(), new BlockModule.BlockStateInfo(blockIndexColumn, chunkRef));
|
||||
farmingBlock = new FarmingBlock();
|
||||
farmingBlock.setLastTickGameTime(now);
|
||||
farmingBlock.setCurrentStageSet(this.targetStageSet != null ? this.targetStageSet : farmingConfig.getStartingStageSet());
|
||||
int initStage = Math.max(0, stages.length - 2);
|
||||
farmingBlock.setGrowthProgress(initStage);
|
||||
blockEntity.addComponent(FarmingBlock.getComponentType(), farmingBlock);
|
||||
blockRef = chunkStore.addEntity(blockEntity, AddReason.SPAWN);
|
||||
LOGGER.atInfo()
|
||||
.log(
|
||||
"[ChangeFarmingStage] Created new block entity with FarmingBlock: stageSet=%s, initialProgress=%d (second-to-last to avoid removal)",
|
||||
farmingBlock.getCurrentStageSet(),
|
||||
initStage
|
||||
);
|
||||
if (blockRef != null) {
|
||||
farmingBlock.setGrowthProgress(stages.length - 1);
|
||||
LOGGER.atInfo().log("[ChangeFarmingStage] Updated growthProgress to %d (actual final stage)", stages.length - 1);
|
||||
}
|
||||
} else {
|
||||
farmingBlock = chunkStore.getComponent(blockRef, FarmingBlock.getComponentType());
|
||||
boolean hadExistingFarmingBlock = farmingBlock != null;
|
||||
LOGGER.atInfo()
|
||||
.log("[ChangeFarmingStage] Block entity exists, FarmingBlock component: %s", hadExistingFarmingBlock ? "exists" : "null");
|
||||
if (farmingBlock == null) {
|
||||
farmingBlock = new FarmingBlock();
|
||||
farmingBlock.setLastTickGameTime(now);
|
||||
farmingBlock.setCurrentStageSet(this.targetStageSet != null ? this.targetStageSet : farmingConfig.getStartingStageSet());
|
||||
farmingBlock.setGrowthProgress(stages.length - 1);
|
||||
chunkStore.putComponent(blockRef, FarmingBlock.getComponentType(), farmingBlock);
|
||||
LOGGER.atInfo()
|
||||
.log(
|
||||
"[ChangeFarmingStage] Added FarmingBlock to existing entity: stageSet=%s, initialProgress=%d",
|
||||
farmingBlock.getCurrentStageSet(),
|
||||
stages.length - 1
|
||||
);
|
||||
} else {
|
||||
LOGGER.atInfo()
|
||||
.log(
|
||||
"[ChangeFarmingStage] Existing FarmingBlock: stageSet=%s, growthProgress=%.2f, lastTickGameTime=%d",
|
||||
farmingBlock.getCurrentStageSet(),
|
||||
farmingBlock.getGrowthProgress(),
|
||||
farmingBlock.getLastTickGameTime()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (blockRef == null) {
|
||||
LOGGER.atWarning().log("[ChangeFarmingStage] FAILED: blockRef is still null after entity creation");
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
String stageSetName = this.targetStageSet != null ? this.targetStageSet : farmingBlock.getCurrentStageSet();
|
||||
stages = farmingConfig.getStages().get(stageSetName);
|
||||
if (stages != null && stages.length != 0) {
|
||||
LOGGER.atInfo().log("[ChangeFarmingStage] Using stageSet=%s with %d stages", stageSetName, stages.length);
|
||||
int currentStage = (int)farmingBlock.getGrowthProgress();
|
||||
int originalCurrentStage = currentStage;
|
||||
if (currentStage >= stages.length) {
|
||||
LOGGER.atInfo()
|
||||
.log("[ChangeFarmingStage] Clamping currentStage from %d to %d (was >= stages.length)", currentStage, stages.length - 1);
|
||||
currentStage = stages.length - 1;
|
||||
}
|
||||
|
||||
int stageIndex;
|
||||
if (this.increaseBy != null) {
|
||||
stageIndex = currentStage + this.increaseBy;
|
||||
LOGGER.atInfo().log("[ChangeFarmingStage] Mode=INCREASE: %d + %d = %d", currentStage, this.increaseBy, stageIndex);
|
||||
} else if (this.decreaseBy != null) {
|
||||
stageIndex = currentStage - this.decreaseBy;
|
||||
LOGGER.atInfo().log("[ChangeFarmingStage] Mode=DECREASE: %d - %d = %d", currentStage, this.decreaseBy, stageIndex);
|
||||
} else {
|
||||
stageIndex = this.targetStage;
|
||||
if (stageIndex < 0) {
|
||||
stageIndex = stages.length - 1;
|
||||
}
|
||||
|
||||
LOGGER.atInfo().log("[ChangeFarmingStage] Mode=ABSOLUTE: targetStage=%d, resolved=%d", this.targetStage, stageIndex);
|
||||
}
|
||||
|
||||
int preClampStageIndex = stageIndex;
|
||||
if (stageIndex < 0) {
|
||||
stageIndex = 0;
|
||||
}
|
||||
|
||||
if (stageIndex >= stages.length) {
|
||||
stageIndex = stages.length - 1;
|
||||
}
|
||||
|
||||
if (preClampStageIndex != stageIndex) {
|
||||
LOGGER.atInfo().log("[ChangeFarmingStage] Clamped stageIndex from %d to %d", preClampStageIndex, stageIndex);
|
||||
}
|
||||
|
||||
int previousStageIndex = (int)farmingBlock.getGrowthProgress();
|
||||
FarmingStageData previousStage = null;
|
||||
FarmingStageData[] currentStages = farmingConfig.getStages().get(farmingBlock.getCurrentStageSet());
|
||||
if (currentStages != null && previousStageIndex >= 0 && previousStageIndex < currentStages.length) {
|
||||
previousStage = currentStages[previousStageIndex];
|
||||
}
|
||||
|
||||
LOGGER.atInfo()
|
||||
.log("[ChangeFarmingStage] Previous stage data: index=%d, hasPreviousStage=%s", previousStageIndex, previousStage != null);
|
||||
farmingBlock.setCurrentStageSet(stageSetName);
|
||||
farmingBlock.setGrowthProgress(stageIndex);
|
||||
farmingBlock.setExecutions(0);
|
||||
farmingBlock.setGeneration(farmingBlock.getGeneration() + 1);
|
||||
farmingBlock.setLastTickGameTime(now);
|
||||
LOGGER.atInfo()
|
||||
.log(
|
||||
"[ChangeFarmingStage] Updated FarmingBlock: stageSet=%s, growthProgress=%d, generation=%d",
|
||||
stageSetName,
|
||||
stageIndex,
|
||||
farmingBlock.getGeneration()
|
||||
);
|
||||
Ref<ChunkStore> sectionRef = world.getChunkStore()
|
||||
.getChunkSectionReference(ChunkUtil.chunkCoordinate(x), ChunkUtil.chunkCoordinate(y), ChunkUtil.chunkCoordinate(z));
|
||||
if (sectionRef != null) {
|
||||
BlockSection section = chunkStore.getComponent(sectionRef, BlockSection.getComponentType());
|
||||
if (section != null) {
|
||||
section.scheduleTick(ChunkUtil.indexBlock(x, y, z), now);
|
||||
}
|
||||
|
||||
stages[stageIndex].apply(chunkStore, sectionRef, blockRef, x, y, z, previousStage);
|
||||
LOGGER.atInfo().log("[ChangeFarmingStage] Applied stage %d via stages[%d].apply()", stageIndex, stageIndex);
|
||||
} else {
|
||||
LOGGER.atWarning().log("[ChangeFarmingStage] sectionRef was null - could not apply stage!");
|
||||
}
|
||||
|
||||
worldChunk.setTicking(x, y, z, true);
|
||||
LOGGER.atInfo()
|
||||
.log(
|
||||
"[ChangeFarmingStage] SUCCESS: Changed stage from %d to %d at pos=(%d, %d, %d)", originalCurrentStage, stageIndex, x, y, z
|
||||
);
|
||||
} else {
|
||||
LOGGER.atWarning().log("[ChangeFarmingStage] FAILED: stages null/empty after re-fetch with stageSet=%s", stageSetName);
|
||||
context.getState().state = InteractionState.Failed;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGGER.atWarning().log("[ChangeFarmingStage] FAILED: stages is null or empty for stageSet=%s", initialStageSetLookup);
|
||||
context.getState().state = InteractionState.Failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGGER.atWarning()
|
||||
.log(
|
||||
"[ChangeFarmingStage] FAILED: farmingConfig is null or has no stages. blockType=%s, hasFarmingConfig=%s",
|
||||
blockType.getId(),
|
||||
farmingConfig != null
|
||||
);
|
||||
context.getState().state = InteractionState.Failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void simulateInteractWithBlock(
|
||||
@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nullable ItemStack itemInHand, @Nonnull World world, @Nonnull Vector3i targetBlock
|
||||
) {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ChangeFarmingStageInteraction{targetStage="
|
||||
+ this.targetStage
|
||||
+ ", increaseBy="
|
||||
+ this.increaseBy
|
||||
+ ", decreaseBy="
|
||||
+ this.decreaseBy
|
||||
+ ", targetStageSet='"
|
||||
+ this.targetStageSet
|
||||
+ "'} "
|
||||
+ super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.interactions;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.TilledSoilBlock;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
import com.hypixel.hytale.math.vector.Vector3i;
|
||||
import com.hypixel.hytale.protocol.InteractionState;
|
||||
import com.hypixel.hytale.protocol.InteractionType;
|
||||
import com.hypixel.hytale.protocol.WaitForDataFrom;
|
||||
import com.hypixel.hytale.server.core.entity.InteractionContext;
|
||||
import com.hypixel.hytale.server.core.inventory.ItemStack;
|
||||
import com.hypixel.hytale.server.core.modules.block.BlockModule;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class FertilizeSoilInteraction extends SimpleBlockInteraction {
|
||||
public static final BuilderCodec<FertilizeSoilInteraction> CODEC = BuilderCodec.builder(
|
||||
FertilizeSoilInteraction.class, FertilizeSoilInteraction::new, SimpleBlockInteraction.CODEC
|
||||
)
|
||||
.documentation("If the target block is farmable then set it to fertilized.")
|
||||
.addField(
|
||||
new KeyedCodec<>("RefreshModifiers", Codec.STRING_ARRAY),
|
||||
(interaction, refreshModifiers) -> interaction.refreshModifiers = refreshModifiers,
|
||||
interaction -> interaction.refreshModifiers
|
||||
)
|
||||
.build();
|
||||
protected String[] refreshModifiers;
|
||||
|
||||
public FertilizeSoilInteraction() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public WaitForDataFrom getWaitForDataFrom() {
|
||||
return WaitForDataFrom.Server;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void interactWithBlock(
|
||||
@Nonnull World world,
|
||||
@Nonnull CommandBuffer<EntityStore> commandBuffer,
|
||||
@Nonnull InteractionType type,
|
||||
@Nonnull InteractionContext context,
|
||||
@Nullable ItemStack itemInHand,
|
||||
@Nonnull Vector3i targetBlock,
|
||||
@Nonnull CooldownHandler cooldownHandler
|
||||
) {
|
||||
int x = targetBlock.getX();
|
||||
int z = targetBlock.getZ();
|
||||
WorldChunk worldChunk = world.getChunk(ChunkUtil.indexChunkFromBlock(x, z));
|
||||
Ref<ChunkStore> blockRef = worldChunk.getBlockComponentEntity(x, targetBlock.getY(), z);
|
||||
if (blockRef == null) {
|
||||
blockRef = BlockModule.ensureBlockEntity(worldChunk, targetBlock.x, targetBlock.y, targetBlock.z);
|
||||
}
|
||||
|
||||
if (blockRef == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
Store<ChunkStore> chunkStore = world.getChunkStore().getStore();
|
||||
TilledSoilBlock soil = chunkStore.getComponent(blockRef, TilledSoilBlock.getComponentType());
|
||||
if (soil != null && !soil.isFertilized()) {
|
||||
soil.setFertilized(true);
|
||||
worldChunk.setTicking(x, targetBlock.getY(), z, true);
|
||||
worldChunk.setTicking(x, targetBlock.getY() + 1, z, true);
|
||||
} else {
|
||||
FarmingBlock farmingState = chunkStore.getComponent(blockRef, FarmingBlock.getComponentType());
|
||||
if (farmingState == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
Ref<ChunkStore> soilRef = worldChunk.getBlockComponentEntity(x, targetBlock.getY() - 1, z);
|
||||
if (soilRef == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
soil = chunkStore.getComponent(soilRef, TilledSoilBlock.getComponentType());
|
||||
if (soil != null && !soil.isFertilized()) {
|
||||
soil.setFertilized(true);
|
||||
worldChunk.setTicking(x, targetBlock.getY() - 1, z, true);
|
||||
worldChunk.setTicking(x, targetBlock.getY(), z, true);
|
||||
} else {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void simulateInteractWithBlock(
|
||||
@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nullable ItemStack itemInHand, @Nonnull World world, @Nonnull Vector3i targetBlock
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.interactions;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.FarmingUtil;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
import com.hypixel.hytale.math.vector.Vector3i;
|
||||
import com.hypixel.hytale.protocol.InteractionType;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
|
||||
import com.hypixel.hytale.server.core.entity.InteractionContext;
|
||||
import com.hypixel.hytale.server.core.inventory.ItemStack;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.BlockChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.section.BlockSection;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class HarvestCropInteraction extends SimpleBlockInteraction {
|
||||
public static final BuilderCodec<HarvestCropInteraction> CODEC = BuilderCodec.builder(
|
||||
HarvestCropInteraction.class, HarvestCropInteraction::new, SimpleBlockInteraction.CODEC
|
||||
)
|
||||
.documentation("Harvests the resources from the target farmable block.")
|
||||
.build();
|
||||
|
||||
public HarvestCropInteraction() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void interactWithBlock(
|
||||
@Nonnull World world,
|
||||
@Nonnull CommandBuffer<EntityStore> commandBuffer,
|
||||
@Nonnull InteractionType type,
|
||||
@Nonnull InteractionContext context,
|
||||
@Nullable ItemStack itemInHand,
|
||||
@Nonnull Vector3i targetBlock,
|
||||
@Nonnull CooldownHandler cooldownHandler
|
||||
) {
|
||||
Ref<EntityStore> ref = context.getEntity();
|
||||
ChunkStore chunkStore = world.getChunkStore();
|
||||
long chunkIndex = ChunkUtil.indexChunkFromBlock(targetBlock.x, targetBlock.z);
|
||||
Ref<ChunkStore> chunkRef = chunkStore.getChunkReference(chunkIndex);
|
||||
if (chunkRef != null && chunkRef.isValid()) {
|
||||
BlockChunk blockChunkComponent = chunkStore.getStore().getComponent(chunkRef, BlockChunk.getComponentType());
|
||||
|
||||
assert blockChunkComponent != null;
|
||||
|
||||
BlockSection section = blockChunkComponent.getSectionAtBlockY(targetBlock.y);
|
||||
if (section != null) {
|
||||
WorldChunk worldChunkComponent = chunkStore.getStore().getComponent(chunkRef, WorldChunk.getComponentType());
|
||||
|
||||
assert worldChunkComponent != null;
|
||||
|
||||
BlockType blockType = worldChunkComponent.getBlockType(targetBlock);
|
||||
if (blockType != null) {
|
||||
int rotationIndex = section.getRotationIndex(targetBlock.x, targetBlock.y, targetBlock.z);
|
||||
FarmingUtil.harvest(world, commandBuffer, ref, blockType, rotationIndex, targetBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void simulateInteractWithBlock(
|
||||
@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nullable ItemStack itemInHand, @Nonnull World world, @Nonnull Vector3i targetBlock
|
||||
) {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HarvestCropInteraction{} " + super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.interactions;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.CoopBlock;
|
||||
import com.hypixel.hytale.builtin.tagset.TagSetPlugin;
|
||||
import com.hypixel.hytale.builtin.tagset.config.NPCGroup;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.RemoveReason;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
import com.hypixel.hytale.math.vector.Vector3d;
|
||||
import com.hypixel.hytale.math.vector.Vector3f;
|
||||
import com.hypixel.hytale.math.vector.Vector3i;
|
||||
import com.hypixel.hytale.protocol.BlockPosition;
|
||||
import com.hypixel.hytale.protocol.InteractionState;
|
||||
import com.hypixel.hytale.protocol.InteractionType;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockFace;
|
||||
import com.hypixel.hytale.server.core.asset.type.model.config.ModelAsset;
|
||||
import com.hypixel.hytale.server.core.entity.EntityUtils;
|
||||
import com.hypixel.hytale.server.core.entity.InteractionContext;
|
||||
import com.hypixel.hytale.server.core.entity.LivingEntity;
|
||||
import com.hypixel.hytale.server.core.inventory.Inventory;
|
||||
import com.hypixel.hytale.server.core.inventory.ItemStack;
|
||||
import com.hypixel.hytale.server.core.modules.block.BlockModule;
|
||||
import com.hypixel.hytale.server.core.modules.entity.component.PersistentModel;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInteraction;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction;
|
||||
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import com.hypixel.hytale.server.npc.NPCPlugin;
|
||||
import com.hypixel.hytale.server.npc.entities.NPCEntity;
|
||||
import com.hypixel.hytale.server.npc.metadata.CapturedNPCMetadata;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class UseCaptureCrateInteraction extends SimpleBlockInteraction {
|
||||
public static final BuilderCodec<UseCaptureCrateInteraction> CODEC = BuilderCodec.builder(
|
||||
UseCaptureCrateInteraction.class, UseCaptureCrateInteraction::new, SimpleInteraction.CODEC
|
||||
)
|
||||
.appendInherited(
|
||||
new KeyedCodec<>("AcceptedNpcGroups", NPCGroup.CHILD_ASSET_CODEC_ARRAY),
|
||||
(o, v) -> o.acceptedNpcGroupIds = v,
|
||||
o -> o.acceptedNpcGroupIds,
|
||||
(o, p) -> o.acceptedNpcGroupIds = p.acceptedNpcGroupIds
|
||||
)
|
||||
.addValidator(NPCGroup.VALIDATOR_CACHE.getArrayValidator())
|
||||
.add()
|
||||
.appendInherited(new KeyedCodec<>("FullIcon", Codec.STRING), (o, v) -> o.fullIcon = v, o -> o.fullIcon, (o, p) -> o.fullIcon = p.fullIcon)
|
||||
.add()
|
||||
.afterDecode(captureData -> {
|
||||
if (captureData.acceptedNpcGroupIds != null) {
|
||||
captureData.acceptedNpcGroupIndexes = new int[captureData.acceptedNpcGroupIds.length];
|
||||
|
||||
for (int i = 0; i < captureData.acceptedNpcGroupIds.length; i++) {
|
||||
int assetIdx = NPCGroup.getAssetMap().getIndex(captureData.acceptedNpcGroupIds[i]);
|
||||
captureData.acceptedNpcGroupIndexes[i] = assetIdx;
|
||||
}
|
||||
}
|
||||
})
|
||||
.build();
|
||||
protected String[] acceptedNpcGroupIds;
|
||||
protected int[] acceptedNpcGroupIndexes;
|
||||
protected String fullIcon;
|
||||
|
||||
public UseCaptureCrateInteraction() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tick0(
|
||||
boolean firstRun, float time, @Nonnull InteractionType type, @Nonnull InteractionContext context, @Nonnull CooldownHandler cooldownHandler
|
||||
) {
|
||||
CommandBuffer<EntityStore> commandBuffer = context.getCommandBuffer();
|
||||
if (commandBuffer == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
ItemStack item = context.getHeldItem();
|
||||
if (item == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
Ref<EntityStore> ref = context.getEntity();
|
||||
if (!(EntityUtils.getEntity(ref, commandBuffer) instanceof LivingEntity livingEntity)) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
Inventory inventory = livingEntity.getInventory();
|
||||
byte activeHotbarSlot = inventory.getActiveHotbarSlot();
|
||||
ItemStack inHandItemStack = inventory.getActiveHotbarItem();
|
||||
CapturedNPCMetadata existingMeta = item.getFromMetadataOrNull("CapturedEntity", CapturedNPCMetadata.CODEC);
|
||||
if (existingMeta != null) {
|
||||
super.tick0(firstRun, time, type, context, cooldownHandler);
|
||||
} else {
|
||||
Ref<EntityStore> targetEntity = context.getTargetEntity();
|
||||
if (targetEntity == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
NPCEntity npcComponent = commandBuffer.getComponent(targetEntity, NPCEntity.getComponentType());
|
||||
if (npcComponent == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
TagSetPlugin.TagSetLookup tagSetPlugin = TagSetPlugin.get(NPCGroup.class);
|
||||
boolean tagFound = false;
|
||||
|
||||
for (int group : this.acceptedNpcGroupIndexes) {
|
||||
if (tagSetPlugin.tagInSet(group, npcComponent.getRoleIndex())) {
|
||||
tagFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tagFound) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
PersistentModel persistentModel = commandBuffer.getComponent(targetEntity, PersistentModel.getComponentType());
|
||||
if (persistentModel == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
ModelAsset modelAsset = ModelAsset.getAssetMap().getAsset(persistentModel.getModelReference().getModelAssetId());
|
||||
CapturedNPCMetadata meta = inHandItemStack.getFromMetadataOrDefault("CapturedEntity", CapturedNPCMetadata.CODEC);
|
||||
if (modelAsset != null) {
|
||||
meta.setIconPath(modelAsset.getIcon());
|
||||
}
|
||||
|
||||
meta.setRoleIndex(npcComponent.getRoleIndex());
|
||||
String npcName = NPCPlugin.get().getName(npcComponent.getRoleIndex());
|
||||
if (npcName != null) {
|
||||
meta.setNpcNameKey(npcName);
|
||||
}
|
||||
|
||||
if (this.fullIcon != null) {
|
||||
meta.setFullItemIcon(this.fullIcon);
|
||||
}
|
||||
|
||||
ItemStack itemWithNPC = inHandItemStack.withMetadata(CapturedNPCMetadata.KEYED_CODEC, meta);
|
||||
inventory.getHotbar().replaceItemStackInSlot(activeHotbarSlot, item, itemWithNPC);
|
||||
commandBuffer.removeEntity(targetEntity, RemoveReason.REMOVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void interactWithBlock(
|
||||
@Nonnull World world,
|
||||
@Nonnull CommandBuffer<EntityStore> commandBuffer,
|
||||
@Nonnull InteractionType type,
|
||||
@Nonnull InteractionContext context,
|
||||
@Nullable ItemStack itemInHand,
|
||||
@Nonnull Vector3i targetBlock,
|
||||
@Nonnull CooldownHandler cooldownHandler
|
||||
) {
|
||||
ItemStack item = context.getHeldItem();
|
||||
if (item == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
Ref<EntityStore> ref = context.getEntity();
|
||||
if (EntityUtils.getEntity(ref, commandBuffer) instanceof LivingEntity livingEntity) {
|
||||
Inventory inventory = livingEntity.getInventory();
|
||||
byte activeHotbarSlot = inventory.getActiveHotbarSlot();
|
||||
CapturedNPCMetadata existingMeta = item.getFromMetadataOrNull("CapturedEntity", CapturedNPCMetadata.CODEC);
|
||||
if (existingMeta == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
BlockPosition pos = context.getTargetBlock();
|
||||
if (pos == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
WorldChunk worldChunk = world.getChunk(ChunkUtil.indexChunkFromBlock(pos.x, pos.z));
|
||||
Ref<ChunkStore> blockRef = worldChunk.getBlockComponentEntity(pos.x, pos.y, pos.z);
|
||||
if (blockRef == null) {
|
||||
blockRef = BlockModule.ensureBlockEntity(worldChunk, pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
ItemStack noMetaItemStack = item.withMetadata(null);
|
||||
if (blockRef != null) {
|
||||
Store<ChunkStore> chunkStore = world.getChunkStore().getStore();
|
||||
CoopBlock coopBlockState = chunkStore.getComponent(blockRef, CoopBlock.getComponentType());
|
||||
if (coopBlockState != null) {
|
||||
WorldTimeResource worldTimeResource = commandBuffer.getResource(WorldTimeResource.getResourceType());
|
||||
if (coopBlockState.tryPutResident(existingMeta, worldTimeResource)) {
|
||||
world.execute(
|
||||
() -> coopBlockState.ensureSpawnResidentsInWorld(
|
||||
world, world.getEntityStore().getStore(), new Vector3d(pos.x, pos.y, pos.z), new Vector3d().assign(Vector3d.FORWARD)
|
||||
)
|
||||
);
|
||||
inventory.getHotbar().replaceItemStackInSlot(activeHotbarSlot, item, noMetaItemStack);
|
||||
} else {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Vector3d spawnPos = new Vector3d(pos.x + 0.5F, pos.y, pos.z + 0.5F);
|
||||
if (context.getClientState() != null) {
|
||||
BlockFace blockFace = BlockFace.fromProtocolFace(context.getClientState().blockFace);
|
||||
if (blockFace != null) {
|
||||
spawnPos.add(blockFace.getDirection());
|
||||
}
|
||||
}
|
||||
|
||||
NPCPlugin npcModule = NPCPlugin.get();
|
||||
Store<EntityStore> store = context.getCommandBuffer().getStore();
|
||||
int roleIndex = existingMeta.getRoleIndex();
|
||||
commandBuffer.run(_store -> npcModule.spawnEntity(store, roleIndex, spawnPos, Vector3f.ZERO, null, null));
|
||||
inventory.getHotbar().replaceItemStackInSlot(activeHotbarSlot, item, noMetaItemStack);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void simulateInteractWithBlock(
|
||||
@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nullable ItemStack itemInHand, @Nonnull World world, @Nonnull Vector3i targetBlock
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.interactions;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.CoopBlock;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
import com.hypixel.hytale.math.vector.Vector3i;
|
||||
import com.hypixel.hytale.protocol.InteractionState;
|
||||
import com.hypixel.hytale.protocol.InteractionType;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
|
||||
import com.hypixel.hytale.server.core.entity.EntityUtils;
|
||||
import com.hypixel.hytale.server.core.entity.InteractionContext;
|
||||
import com.hypixel.hytale.server.core.entity.LivingEntity;
|
||||
import com.hypixel.hytale.server.core.inventory.ItemStack;
|
||||
import com.hypixel.hytale.server.core.inventory.container.CombinedItemContainer;
|
||||
import com.hypixel.hytale.server.core.modules.block.BlockModule;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class UseCoopInteraction extends SimpleBlockInteraction {
|
||||
public static final BuilderCodec<UseCoopInteraction> CODEC = BuilderCodec.builder(
|
||||
UseCoopInteraction.class, UseCoopInteraction::new, SimpleBlockInteraction.CODEC
|
||||
)
|
||||
.build();
|
||||
|
||||
public UseCoopInteraction() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void interactWithBlock(
|
||||
@Nonnull World world,
|
||||
@Nonnull CommandBuffer<EntityStore> commandBuffer,
|
||||
@Nonnull InteractionType type,
|
||||
@Nonnull InteractionContext context,
|
||||
@Nullable ItemStack itemInHand,
|
||||
@Nonnull Vector3i targetBlock,
|
||||
@Nonnull CooldownHandler cooldownHandler
|
||||
) {
|
||||
int x = targetBlock.getX();
|
||||
int z = targetBlock.getZ();
|
||||
WorldChunk worldChunk = world.getChunk(ChunkUtil.indexChunkFromBlock(x, z));
|
||||
if (worldChunk == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
Ref<ChunkStore> blockRef = worldChunk.getBlockComponentEntity(x, targetBlock.getY(), z);
|
||||
if (blockRef == null) {
|
||||
blockRef = BlockModule.ensureBlockEntity(worldChunk, targetBlock.x, targetBlock.y, targetBlock.z);
|
||||
}
|
||||
|
||||
if (blockRef == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
Store<ChunkStore> chunkStore = world.getChunkStore().getStore();
|
||||
CoopBlock coopBlockState = chunkStore.getComponent(blockRef, CoopBlock.getComponentType());
|
||||
if (coopBlockState == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
Ref<EntityStore> playerRef = context.getEntity();
|
||||
LivingEntity playerEntity = (LivingEntity)EntityUtils.getEntity(playerRef, commandBuffer);
|
||||
if (playerEntity == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
CombinedItemContainer playerInventoryContainer = playerEntity.getInventory().getCombinedHotbarFirst();
|
||||
if (playerInventoryContainer != null) {
|
||||
coopBlockState.gatherProduceFromInventory(playerInventoryContainer);
|
||||
BlockType currentBlockType = worldChunk.getBlockType(targetBlock);
|
||||
|
||||
assert currentBlockType != null;
|
||||
|
||||
worldChunk.setBlockInteractionState(targetBlock, currentBlockType, coopBlockState.hasProduce() ? "Produce_Ready" : "default");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void simulateInteractWithBlock(
|
||||
@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nullable ItemStack itemInHand, @Nonnull World world, @Nonnull Vector3i targetBlock
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.interactions;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.FarmingBlock;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.states.TilledSoilBlock;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.math.util.ChunkUtil;
|
||||
import com.hypixel.hytale.math.vector.Vector3i;
|
||||
import com.hypixel.hytale.protocol.InteractionState;
|
||||
import com.hypixel.hytale.protocol.InteractionType;
|
||||
import com.hypixel.hytale.protocol.WaitForDataFrom;
|
||||
import com.hypixel.hytale.server.core.entity.InteractionContext;
|
||||
import com.hypixel.hytale.server.core.inventory.ItemStack;
|
||||
import com.hypixel.hytale.server.core.modules.block.BlockModule;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.client.SimpleBlockInteraction;
|
||||
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.chunk.WorldChunk;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class UseWateringCanInteraction extends SimpleBlockInteraction {
|
||||
public static final BuilderCodec<UseWateringCanInteraction> CODEC = BuilderCodec.builder(
|
||||
UseWateringCanInteraction.class, UseWateringCanInteraction::new, SimpleBlockInteraction.CODEC
|
||||
)
|
||||
.documentation("Waters the target farmable block.")
|
||||
.addField(new KeyedCodec<>("Duration", Codec.LONG), (interaction, duration) -> interaction.duration = duration, interaction -> interaction.duration)
|
||||
.addField(
|
||||
new KeyedCodec<>("RefreshModifiers", Codec.STRING_ARRAY),
|
||||
(interaction, refreshModifiers) -> interaction.refreshModifiers = refreshModifiers,
|
||||
interaction -> interaction.refreshModifiers
|
||||
)
|
||||
.build();
|
||||
protected long duration;
|
||||
protected String[] refreshModifiers;
|
||||
|
||||
public UseWateringCanInteraction() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public WaitForDataFrom getWaitForDataFrom() {
|
||||
return WaitForDataFrom.Server;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void interactWithBlock(
|
||||
@Nonnull World world,
|
||||
@Nonnull CommandBuffer<EntityStore> commandBuffer,
|
||||
@Nonnull InteractionType type,
|
||||
@Nonnull InteractionContext context,
|
||||
@Nullable ItemStack itemInHand,
|
||||
@Nonnull Vector3i targetBlock,
|
||||
@Nonnull CooldownHandler cooldownHandler
|
||||
) {
|
||||
int x = targetBlock.getX();
|
||||
int z = targetBlock.getZ();
|
||||
WorldChunk worldChunk = world.getChunk(ChunkUtil.indexChunkFromBlock(x, z));
|
||||
Ref<ChunkStore> blockRef = worldChunk.getBlockComponentEntity(x, targetBlock.getY(), z);
|
||||
if (blockRef == null) {
|
||||
blockRef = BlockModule.ensureBlockEntity(worldChunk, targetBlock.x, targetBlock.y, targetBlock.z);
|
||||
}
|
||||
|
||||
if (blockRef == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
Store<ChunkStore> chunkStore = world.getChunkStore().getStore();
|
||||
WorldTimeResource worldTimeResource = commandBuffer.getResource(WorldTimeResource.getResourceType());
|
||||
TilledSoilBlock soil = chunkStore.getComponent(blockRef, TilledSoilBlock.getComponentType());
|
||||
if (soil != null) {
|
||||
Instant wateredUntil = worldTimeResource.getGameTime().plus(this.duration, ChronoUnit.SECONDS);
|
||||
soil.setWateredUntil(wateredUntil);
|
||||
worldChunk.setTicking(x, targetBlock.getY(), z, true);
|
||||
worldChunk.getBlockChunk().getSectionAtBlockY(targetBlock.y).scheduleTick(ChunkUtil.indexBlock(x, targetBlock.y, z), wateredUntil);
|
||||
worldChunk.setTicking(x, targetBlock.getY() + 1, z, true);
|
||||
} else {
|
||||
FarmingBlock farmingState = chunkStore.getComponent(blockRef, FarmingBlock.getComponentType());
|
||||
if (farmingState == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
Ref<ChunkStore> soilRef = worldChunk.getBlockComponentEntity(x, targetBlock.getY() - 1, z);
|
||||
if (soilRef == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
soil = chunkStore.getComponent(soilRef, TilledSoilBlock.getComponentType());
|
||||
if (soil == null) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
Instant wateredUntil = worldTimeResource.getGameTime().plus(this.duration, ChronoUnit.SECONDS);
|
||||
soil.setWateredUntil(wateredUntil);
|
||||
worldChunk.getBlockChunk().getSectionAtBlockY(targetBlock.y - 1).scheduleTick(ChunkUtil.indexBlock(x, targetBlock.y - 1, z), wateredUntil);
|
||||
worldChunk.setTicking(x, targetBlock.getY() - 1, z, true);
|
||||
worldChunk.setTicking(x, targetBlock.getY(), z, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void simulateInteractWithBlock(
|
||||
@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nullable ItemStack itemInHand, @Nonnull World world, @Nonnull Vector3i targetBlock
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,477 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.states;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.FarmingPlugin;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.FarmingUtil;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.component.CoopResidentComponent;
|
||||
import com.hypixel.hytale.builtin.adventure.farming.config.FarmingCoopAsset;
|
||||
import com.hypixel.hytale.builtin.tagset.TagSetPlugin;
|
||||
import com.hypixel.hytale.builtin.tagset.config.NPCGroup;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
|
||||
import com.hypixel.hytale.component.AddReason;
|
||||
import com.hypixel.hytale.component.Component;
|
||||
import com.hypixel.hytale.component.ComponentType;
|
||||
import com.hypixel.hytale.component.Holder;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.logger.HytaleLogger;
|
||||
import com.hypixel.hytale.math.range.IntRange;
|
||||
import com.hypixel.hytale.math.util.MathUtil;
|
||||
import com.hypixel.hytale.math.vector.Vector3d;
|
||||
import com.hypixel.hytale.math.vector.Vector3f;
|
||||
import com.hypixel.hytale.math.vector.Vector3i;
|
||||
import com.hypixel.hytale.server.core.asset.type.item.config.ItemDrop;
|
||||
import com.hypixel.hytale.server.core.asset.type.item.config.ItemDropList;
|
||||
import com.hypixel.hytale.server.core.entity.UUIDComponent;
|
||||
import com.hypixel.hytale.server.core.entity.reference.PersistentRef;
|
||||
import com.hypixel.hytale.server.core.inventory.ItemStack;
|
||||
import com.hypixel.hytale.server.core.inventory.container.EmptyItemContainer;
|
||||
import com.hypixel.hytale.server.core.inventory.container.ItemContainer;
|
||||
import com.hypixel.hytale.server.core.inventory.container.SimpleItemContainer;
|
||||
import com.hypixel.hytale.server.core.modules.entity.damage.DeathComponent;
|
||||
import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent;
|
||||
import com.hypixel.hytale.server.core.modules.time.WorldTimeResource;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import com.hypixel.hytale.server.npc.NPCPlugin;
|
||||
import com.hypixel.hytale.server.npc.asset.builder.Builder;
|
||||
import com.hypixel.hytale.server.npc.entities.NPCEntity;
|
||||
import com.hypixel.hytale.server.npc.metadata.CapturedNPCMetadata;
|
||||
import com.hypixel.hytale.server.npc.role.Role;
|
||||
import com.hypixel.hytale.server.spawning.ISpawnableWithModel;
|
||||
import com.hypixel.hytale.server.spawning.SpawnTestResult;
|
||||
import com.hypixel.hytale.server.spawning.SpawningContext;
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class CoopBlock implements Component<ChunkStore> {
|
||||
public static final String STATE_PRODUCE = "Produce_Ready";
|
||||
public static final BuilderCodec<CoopBlock> CODEC = BuilderCodec.builder(CoopBlock.class, CoopBlock::new)
|
||||
.append(new KeyedCodec<>("FarmingCoopId", Codec.STRING, true), (coop, s) -> coop.coopAssetId = s, coop -> coop.coopAssetId)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("Residents", new ArrayCodec<>(CoopBlock.CoopResident.CODEC, CoopBlock.CoopResident[]::new)),
|
||||
(coop, residents) -> coop.residents = new ArrayList<>(Arrays.asList(residents)),
|
||||
coop -> coop.residents.toArray(CoopBlock.CoopResident[]::new)
|
||||
)
|
||||
.add()
|
||||
.append(new KeyedCodec<>("Storage", ItemContainer.CODEC), (coop, storage) -> coop.itemContainer = storage, coop -> coop.itemContainer)
|
||||
.add()
|
||||
.build();
|
||||
protected String coopAssetId;
|
||||
protected List<CoopBlock.CoopResident> residents = new ArrayList<>();
|
||||
protected ItemContainer itemContainer = EmptyItemContainer.INSTANCE;
|
||||
|
||||
public static ComponentType<ChunkStore, CoopBlock> getComponentType() {
|
||||
return FarmingPlugin.get().getCoopBlockStateComponentType();
|
||||
}
|
||||
|
||||
public CoopBlock() {
|
||||
ArrayList<ItemStack> remainder = new ArrayList<>();
|
||||
this.itemContainer = ItemContainer.ensureContainerCapacity(this.itemContainer, (short)5, SimpleItemContainer::new, remainder);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public FarmingCoopAsset getCoopAsset() {
|
||||
return FarmingCoopAsset.getAssetMap().getAsset(this.coopAssetId);
|
||||
}
|
||||
|
||||
public CoopBlock(String farmingCoopId, List<CoopBlock.CoopResident> residents, ItemContainer itemContainer) {
|
||||
this.coopAssetId = farmingCoopId;
|
||||
this.residents.addAll(residents);
|
||||
this.itemContainer = itemContainer.clone();
|
||||
List<ItemStack> remainder = new ObjectArrayList<>();
|
||||
this.itemContainer = ItemContainer.ensureContainerCapacity(this.itemContainer, (short)5, SimpleItemContainer::new, remainder);
|
||||
}
|
||||
|
||||
public boolean tryPutResident(CapturedNPCMetadata metadata, WorldTimeResource worldTimeResource) {
|
||||
FarmingCoopAsset coopAsset = this.getCoopAsset();
|
||||
if (coopAsset == null) {
|
||||
return false;
|
||||
} else if (this.residents.size() >= coopAsset.getMaxResidents()) {
|
||||
return false;
|
||||
} else if (!this.getCoopAcceptsNPCGroup(metadata.getRoleIndex())) {
|
||||
return false;
|
||||
} else {
|
||||
this.residents.add(new CoopBlock.CoopResident(metadata, null, worldTimeResource.getGameTime()));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean tryPutWildResidentFromWild(Store<EntityStore> store, Ref<EntityStore> entityRef, WorldTimeResource worldTimeResource, Vector3i coopLocation) {
|
||||
FarmingCoopAsset coopAsset = this.getCoopAsset();
|
||||
if (coopAsset == null) {
|
||||
return false;
|
||||
} else {
|
||||
NPCEntity npcComponent = store.getComponent(entityRef, NPCEntity.getComponentType());
|
||||
if (npcComponent == null) {
|
||||
return false;
|
||||
} else {
|
||||
CoopResidentComponent coopResidentComponent = store.getComponent(entityRef, CoopResidentComponent.getComponentType());
|
||||
if (coopResidentComponent != null) {
|
||||
return false;
|
||||
} else if (!this.getCoopAcceptsNPCGroup(npcComponent.getRoleIndex())) {
|
||||
return false;
|
||||
} else if (this.residents.size() >= coopAsset.getMaxResidents()) {
|
||||
return false;
|
||||
} else {
|
||||
coopResidentComponent = store.ensureAndGetComponent(entityRef, CoopResidentComponent.getComponentType());
|
||||
coopResidentComponent.setCoopLocation(coopLocation);
|
||||
UUIDComponent uuidComponent = store.getComponent(entityRef, UUIDComponent.getComponentType());
|
||||
if (uuidComponent == null) {
|
||||
return false;
|
||||
} else {
|
||||
PersistentRef persistentRef = new PersistentRef();
|
||||
persistentRef.setEntity(entityRef, uuidComponent.getUuid());
|
||||
CapturedNPCMetadata metadata = FarmingUtil.generateCapturedNPCMetadata(store, entityRef, npcComponent.getRoleIndex());
|
||||
CoopBlock.CoopResident residentRecord = new CoopBlock.CoopResident(metadata, persistentRef, worldTimeResource.getGameTime());
|
||||
residentRecord.deployedToWorld = true;
|
||||
this.residents.add(residentRecord);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getCoopAcceptsNPCGroup(int npcRoleIndex) {
|
||||
TagSetPlugin.TagSetLookup tagSetPlugin = TagSetPlugin.get(NPCGroup.class);
|
||||
FarmingCoopAsset coopAsset = this.getCoopAsset();
|
||||
if (coopAsset == null) {
|
||||
return false;
|
||||
} else {
|
||||
int[] acceptedNpcGroupIndexes = coopAsset.getAcceptedNpcGroupIndexes();
|
||||
if (acceptedNpcGroupIndexes == null) {
|
||||
return true;
|
||||
} else {
|
||||
for (int group : acceptedNpcGroupIndexes) {
|
||||
if (tagSetPlugin.tagInSet(group, npcRoleIndex)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void generateProduceToInventory(WorldTimeResource worldTimeResource) {
|
||||
Instant currentTime = worldTimeResource.getGameTime();
|
||||
FarmingCoopAsset coopAsset = this.getCoopAsset();
|
||||
if (coopAsset != null) {
|
||||
Map<String, String> produceDropsMap = coopAsset.getProduceDrops();
|
||||
if (!produceDropsMap.isEmpty()) {
|
||||
ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||
List<ItemStack> generatedItemDrops = new ArrayList<>();
|
||||
|
||||
for (CoopBlock.CoopResident resident : this.residents) {
|
||||
Instant lastProduced = resident.getLastProduced();
|
||||
if (lastProduced == null) {
|
||||
resident.setLastProduced(currentTime);
|
||||
} else {
|
||||
CapturedNPCMetadata residentMeta = resident.getMetadata();
|
||||
int npcRoleIndex = residentMeta.getRoleIndex();
|
||||
String npcName = NPCPlugin.get().getName(npcRoleIndex);
|
||||
String npcDropListName = produceDropsMap.get(npcName);
|
||||
if (npcDropListName != null) {
|
||||
ItemDropList dropListAsset = ItemDropList.getAssetMap().getAsset(npcDropListName);
|
||||
if (dropListAsset != null) {
|
||||
Duration harvestDiff = Duration.between(lastProduced, currentTime);
|
||||
long hoursSinceLastHarvest = harvestDiff.toHours();
|
||||
int produceCount = MathUtil.ceil((float)hoursSinceLastHarvest / WorldTimeResource.HOURS_PER_DAY);
|
||||
List<ItemDrop> configuredItemDrops = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < produceCount; i++) {
|
||||
dropListAsset.getContainer().populateDrops(configuredItemDrops, random::nextDouble, npcDropListName);
|
||||
|
||||
for (ItemDrop drop : configuredItemDrops) {
|
||||
if (drop != null && drop.getItemId() != null) {
|
||||
int amount = drop.getRandomQuantity(random);
|
||||
if (amount > 0) {
|
||||
generatedItemDrops.add(new ItemStack(drop.getItemId(), amount, drop.getMetadata()));
|
||||
}
|
||||
} else {
|
||||
HytaleLogger.forEnclosingClass()
|
||||
.atWarning()
|
||||
.log("Tried to create ItemDrop for non-existent item in drop list id '%s'", npcDropListName);
|
||||
}
|
||||
}
|
||||
|
||||
configuredItemDrops.clear();
|
||||
}
|
||||
|
||||
resident.setLastProduced(currentTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.itemContainer.addItemStacks(generatedItemDrops);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void gatherProduceFromInventory(ItemContainer playerInventory) {
|
||||
for (ItemStack item : this.itemContainer.removeAllItemStacks()) {
|
||||
playerInventory.addItemStack(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void ensureSpawnResidentsInWorld(World world, Store<EntityStore> store, Vector3d coopLocation, Vector3d spawnOffset) {
|
||||
NPCPlugin npcModule = NPCPlugin.get();
|
||||
FarmingCoopAsset coopAsset = this.getCoopAsset();
|
||||
if (coopAsset != null) {
|
||||
float radiansPerSpawn = (float) (Math.PI * 2) / coopAsset.getMaxResidents();
|
||||
Vector3d spawnOffsetIteration = spawnOffset;
|
||||
SpawningContext spawningContext = new SpawningContext();
|
||||
|
||||
for (CoopBlock.CoopResident resident : this.residents) {
|
||||
CapturedNPCMetadata residentMeta = resident.getMetadata();
|
||||
int npcRoleIndex = residentMeta.getRoleIndex();
|
||||
boolean residentDeployed = resident.getDeployedToWorld();
|
||||
PersistentRef residentEntityId = resident.getPersistentRef();
|
||||
if (!residentDeployed && residentEntityId == null) {
|
||||
Vector3d residentSpawnLocation = new Vector3d().assign(coopLocation).add(spawnOffsetIteration);
|
||||
Builder<Role> roleBuilder = NPCPlugin.get().tryGetCachedValidRole(npcRoleIndex);
|
||||
if (roleBuilder != null) {
|
||||
spawningContext.setSpawnable((ISpawnableWithModel)roleBuilder);
|
||||
if (spawningContext.set(world, residentSpawnLocation.x, residentSpawnLocation.y, residentSpawnLocation.z)
|
||||
&& spawningContext.canSpawn() == SpawnTestResult.TEST_OK) {
|
||||
Pair<Ref<EntityStore>, NPCEntity> npcPair = npcModule.spawnEntity(
|
||||
store, npcRoleIndex, spawningContext.newPosition(), Vector3f.ZERO, null, null
|
||||
);
|
||||
if (npcPair == null) {
|
||||
resident.setPersistentRef(null);
|
||||
resident.setDeployedToWorld(false);
|
||||
} else {
|
||||
Ref<EntityStore> npcRef = npcPair.first();
|
||||
NPCEntity npcComponent = npcPair.second();
|
||||
npcComponent.getLeashPoint().assign(coopLocation);
|
||||
if (npcRef != null && npcRef.isValid()) {
|
||||
UUIDComponent uuidComponent = store.getComponent(npcRef, UUIDComponent.getComponentType());
|
||||
if (uuidComponent == null) {
|
||||
resident.setPersistentRef(null);
|
||||
resident.setDeployedToWorld(false);
|
||||
} else {
|
||||
CoopResidentComponent coopResidentComponent = new CoopResidentComponent();
|
||||
coopResidentComponent.setCoopLocation(coopLocation.toVector3i());
|
||||
store.addComponent(npcRef, CoopResidentComponent.getComponentType(), coopResidentComponent);
|
||||
PersistentRef persistentRef = new PersistentRef();
|
||||
persistentRef.setEntity(npcRef, uuidComponent.getUuid());
|
||||
resident.setPersistentRef(persistentRef);
|
||||
resident.setDeployedToWorld(true);
|
||||
spawnOffsetIteration = spawnOffsetIteration.rotateY(radiansPerSpawn);
|
||||
}
|
||||
} else {
|
||||
resident.setPersistentRef(null);
|
||||
resident.setDeployedToWorld(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ensureNoResidentsInWorld(Store<EntityStore> store) {
|
||||
ArrayList<CoopBlock.CoopResident> residentsToRemove = new ArrayList<>();
|
||||
|
||||
for (CoopBlock.CoopResident resident : this.residents) {
|
||||
boolean deployed = resident.getDeployedToWorld();
|
||||
PersistentRef entityUuid = resident.getPersistentRef();
|
||||
if (deployed || entityUuid != null) {
|
||||
Ref<EntityStore> entityRef = entityUuid.getEntity(store);
|
||||
if (entityRef == null) {
|
||||
residentsToRemove.add(resident);
|
||||
} else {
|
||||
CoopResidentComponent coopResidentComponent = store.getComponent(entityRef, CoopResidentComponent.getComponentType());
|
||||
if (coopResidentComponent == null) {
|
||||
residentsToRemove.add(resident);
|
||||
} else {
|
||||
DeathComponent deathComponent = store.getComponent(entityRef, DeathComponent.getComponentType());
|
||||
if (deathComponent != null) {
|
||||
residentsToRemove.add(resident);
|
||||
} else {
|
||||
coopResidentComponent.setMarkedForDespawn(true);
|
||||
resident.setPersistentRef(null);
|
||||
resident.setDeployedToWorld(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (CoopBlock.CoopResident residentx : residentsToRemove) {
|
||||
this.residents.remove(residentx);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean shouldResidentsBeInCoop(WorldTimeResource worldTimeResource) {
|
||||
FarmingCoopAsset coopAsset = this.getCoopAsset();
|
||||
if (coopAsset == null) {
|
||||
return true;
|
||||
} else {
|
||||
IntRange roamTimeRange = coopAsset.getResidentRoamTime();
|
||||
if (roamTimeRange == null) {
|
||||
return true;
|
||||
} else {
|
||||
int gameHour = worldTimeResource.getCurrentHour();
|
||||
return !roamTimeRange.includes(gameHour);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Instant getNextScheduledTick(WorldTimeResource worldTimeResource) {
|
||||
Instant gameTime = worldTimeResource.getGameTime();
|
||||
LocalDateTime gameDateTime = worldTimeResource.getGameDateTime();
|
||||
int gameHour = worldTimeResource.getCurrentHour();
|
||||
int minutes = gameDateTime.getMinute();
|
||||
FarmingCoopAsset coopAsset = this.getCoopAsset();
|
||||
if (coopAsset == null) {
|
||||
return null;
|
||||
} else {
|
||||
IntRange roamTimeRange = coopAsset.getResidentRoamTime();
|
||||
if (roamTimeRange == null) {
|
||||
return null;
|
||||
} else {
|
||||
int nextScheduledHour = 0;
|
||||
int minTime = roamTimeRange.getInclusiveMin();
|
||||
int maxTime = roamTimeRange.getInclusiveMax();
|
||||
if (coopAsset.getResidentRoamTime().includes(gameHour)) {
|
||||
nextScheduledHour = coopAsset.getResidentRoamTime().getInclusiveMax() + 1 - gameHour;
|
||||
} else if (gameHour > maxTime) {
|
||||
nextScheduledHour = WorldTimeResource.HOURS_PER_DAY - gameHour + minTime;
|
||||
} else {
|
||||
nextScheduledHour = minTime - gameHour;
|
||||
}
|
||||
|
||||
return gameTime.plus(nextScheduledHour * 60L - minutes, ChronoUnit.MINUTES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleResidentDespawn(UUID entityUuid) {
|
||||
CoopBlock.CoopResident removedResident = null;
|
||||
|
||||
for (CoopBlock.CoopResident resident : this.residents) {
|
||||
if (resident.persistentRef != null && resident.persistentRef.getUuid() == entityUuid) {
|
||||
removedResident = resident;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (removedResident != null) {
|
||||
this.residents.remove(removedResident);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleBlockBroken(World world, WorldTimeResource worldTimeResource, Store<EntityStore> store, int blockX, int blockY, int blockZ) {
|
||||
Vector3i location = new Vector3i(blockX, blockY, blockZ);
|
||||
world.execute(() -> this.ensureSpawnResidentsInWorld(world, store, location.toVector3d(), new Vector3d().assign(Vector3d.FORWARD)));
|
||||
this.generateProduceToInventory(worldTimeResource);
|
||||
Vector3d dropPosition = new Vector3d(blockX + 0.5F, blockY, blockZ + 0.5F);
|
||||
Holder<EntityStore>[] itemEntityHolders = ItemComponent.generateItemDrops(store, this.itemContainer.removeAllItemStacks(), dropPosition, Vector3f.ZERO);
|
||||
if (itemEntityHolders.length > 0) {
|
||||
world.execute(() -> store.addEntities(itemEntityHolders, AddReason.SPAWN));
|
||||
}
|
||||
|
||||
world.execute(() -> {
|
||||
for (CoopBlock.CoopResident resident : this.residents) {
|
||||
PersistentRef persistentRef = resident.getPersistentRef();
|
||||
if (persistentRef != null) {
|
||||
Ref<EntityStore> ref = persistentRef.getEntity(store);
|
||||
if (ref == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
store.tryRemoveComponent(ref, CoopResidentComponent.getComponentType());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean hasProduce() {
|
||||
return !this.itemContainer.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component<ChunkStore> clone() {
|
||||
return new CoopBlock(this.coopAssetId, this.residents, this.itemContainer);
|
||||
}
|
||||
|
||||
public static class CoopResident {
|
||||
public static final BuilderCodec<CoopBlock.CoopResident> CODEC = BuilderCodec.builder(CoopBlock.CoopResident.class, CoopBlock.CoopResident::new)
|
||||
.append(new KeyedCodec<>("Metadata", CapturedNPCMetadata.CODEC), (coop, meta) -> coop.metadata = meta, coop -> coop.metadata)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("PersistentRef", PersistentRef.CODEC), (coop, persistentRef) -> coop.persistentRef = persistentRef, coop -> coop.persistentRef
|
||||
)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("DeployedToWorld", Codec.BOOLEAN), (coop, deployedToWorld) -> coop.deployedToWorld = deployedToWorld, coop -> coop.deployedToWorld
|
||||
)
|
||||
.add()
|
||||
.append(new KeyedCodec<>("LastHarvested", Codec.INSTANT), (coop, instant) -> coop.lastProduced = instant, coop -> coop.lastProduced)
|
||||
.add()
|
||||
.build();
|
||||
protected CapturedNPCMetadata metadata;
|
||||
@Nullable
|
||||
protected PersistentRef persistentRef;
|
||||
protected boolean deployedToWorld;
|
||||
protected Instant lastProduced;
|
||||
|
||||
public CoopResident() {
|
||||
}
|
||||
|
||||
public CoopResident(CapturedNPCMetadata metadata, PersistentRef persistentRef, Instant lastProduced) {
|
||||
this.metadata = metadata;
|
||||
this.persistentRef = persistentRef;
|
||||
this.lastProduced = lastProduced;
|
||||
}
|
||||
|
||||
public CapturedNPCMetadata getMetadata() {
|
||||
return this.metadata;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PersistentRef getPersistentRef() {
|
||||
return this.persistentRef;
|
||||
}
|
||||
|
||||
public void setPersistentRef(@Nullable PersistentRef persistentRef) {
|
||||
this.persistentRef = persistentRef;
|
||||
}
|
||||
|
||||
public boolean getDeployedToWorld() {
|
||||
return this.deployedToWorld;
|
||||
}
|
||||
|
||||
public void setDeployedToWorld(boolean deployedToWorld) {
|
||||
this.deployedToWorld = deployedToWorld;
|
||||
}
|
||||
|
||||
public Instant getLastProduced() {
|
||||
return this.lastProduced;
|
||||
}
|
||||
|
||||
public void setLastProduced(Instant lastProduced) {
|
||||
this.lastProduced = lastProduced;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.states;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.FarmingPlugin;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.component.Component;
|
||||
import com.hypixel.hytale.component.ComponentType;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import java.time.Instant;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class FarmingBlock implements Component<ChunkStore> {
|
||||
public static final String DEFAULT_STAGE_SET = "Default";
|
||||
public static final BuilderCodec<FarmingBlock> CODEC = BuilderCodec.builder(FarmingBlock.class, FarmingBlock::new)
|
||||
.append(
|
||||
new KeyedCodec<>("CurrentStageSet", Codec.STRING),
|
||||
(farmingBlock, currentStageSet) -> farmingBlock.currentStageSet = currentStageSet,
|
||||
farmingBlock -> "Default".equals(farmingBlock.currentStageSet) ? null : "Default"
|
||||
)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("GrowthProgress", Codec.FLOAT),
|
||||
(farmingBlock, growthProgress) -> farmingBlock.growthProgress = growthProgress,
|
||||
farmingBlock -> farmingBlock.growthProgress == 0.0F ? null : farmingBlock.growthProgress
|
||||
)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("LastTickGameTime", Codec.INSTANT),
|
||||
(farmingBlock, lastTickGameTime) -> farmingBlock.lastTickGameTime = lastTickGameTime,
|
||||
farmingBlock -> farmingBlock.lastTickGameTime
|
||||
)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("Generation", Codec.INTEGER),
|
||||
(farmingBlock, generation) -> farmingBlock.generation = generation,
|
||||
farmingBlock -> farmingBlock.generation == 0 ? null : farmingBlock.generation
|
||||
)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("PreviousBlockType", Codec.STRING),
|
||||
(farmingBlock, previousBlockType) -> farmingBlock.previousBlockType = previousBlockType,
|
||||
farmingBlock -> farmingBlock.previousBlockType
|
||||
)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("SpreadRate", Codec.FLOAT),
|
||||
(farmingBlock, spreadRate) -> farmingBlock.spreadRate = spreadRate,
|
||||
farmingBlock -> farmingBlock.spreadRate == 1.0F ? null : farmingBlock.spreadRate
|
||||
)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("Executions", Codec.INTEGER),
|
||||
(farmingBlock, executions) -> farmingBlock.executions = executions,
|
||||
farmingBlock -> farmingBlock.executions == 0 ? null : farmingBlock.executions
|
||||
)
|
||||
.add()
|
||||
.build();
|
||||
private String currentStageSet = "Default";
|
||||
private float growthProgress;
|
||||
private Instant lastTickGameTime;
|
||||
private int generation;
|
||||
private String previousBlockType;
|
||||
private float spreadRate = 1.0F;
|
||||
private int executions = 0;
|
||||
|
||||
public static ComponentType<ChunkStore, FarmingBlock> getComponentType() {
|
||||
return FarmingPlugin.get().getFarmingBlockComponentType();
|
||||
}
|
||||
|
||||
public FarmingBlock() {
|
||||
}
|
||||
|
||||
public FarmingBlock(
|
||||
String currentStageSet, float growthProgress, Instant lastTickGameTime, int generation, String previousBlockType, float spreadRate, int executions
|
||||
) {
|
||||
this.currentStageSet = currentStageSet;
|
||||
this.growthProgress = growthProgress;
|
||||
this.lastTickGameTime = lastTickGameTime;
|
||||
this.generation = generation;
|
||||
this.previousBlockType = previousBlockType;
|
||||
this.spreadRate = spreadRate;
|
||||
this.executions = executions;
|
||||
}
|
||||
|
||||
public String getCurrentStageSet() {
|
||||
return this.currentStageSet;
|
||||
}
|
||||
|
||||
public void setCurrentStageSet(String currentStageSet) {
|
||||
this.currentStageSet = currentStageSet != null ? currentStageSet : "Default";
|
||||
}
|
||||
|
||||
public float getGrowthProgress() {
|
||||
return this.growthProgress;
|
||||
}
|
||||
|
||||
public void setGrowthProgress(float growthProgress) {
|
||||
this.growthProgress = growthProgress;
|
||||
}
|
||||
|
||||
public Instant getLastTickGameTime() {
|
||||
return this.lastTickGameTime;
|
||||
}
|
||||
|
||||
public void setLastTickGameTime(Instant lastTickGameTime) {
|
||||
this.lastTickGameTime = lastTickGameTime;
|
||||
}
|
||||
|
||||
public int getGeneration() {
|
||||
return this.generation;
|
||||
}
|
||||
|
||||
public void setGeneration(int generation) {
|
||||
this.generation = generation;
|
||||
}
|
||||
|
||||
public String getPreviousBlockType() {
|
||||
return this.previousBlockType;
|
||||
}
|
||||
|
||||
public void setPreviousBlockType(String previousBlockType) {
|
||||
this.previousBlockType = previousBlockType;
|
||||
}
|
||||
|
||||
public float getSpreadRate() {
|
||||
return this.spreadRate;
|
||||
}
|
||||
|
||||
public void setSpreadRate(float spreadRate) {
|
||||
this.spreadRate = spreadRate;
|
||||
}
|
||||
|
||||
public int getExecutions() {
|
||||
return this.executions;
|
||||
}
|
||||
|
||||
public void setExecutions(int executions) {
|
||||
this.executions = executions;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Component<ChunkStore> clone() {
|
||||
return new FarmingBlock(
|
||||
this.currentStageSet, this.growthProgress, this.lastTickGameTime, this.generation, this.previousBlockType, this.spreadRate, this.executions
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FarmingBlock{currentStageSet='"
|
||||
+ this.currentStageSet
|
||||
+ "', growthProgress="
|
||||
+ this.growthProgress
|
||||
+ ", lastTickGameTime="
|
||||
+ this.lastTickGameTime
|
||||
+ ", generation="
|
||||
+ this.generation
|
||||
+ ", previousBlockType='"
|
||||
+ this.previousBlockType
|
||||
+ "', spreadRate="
|
||||
+ this.spreadRate
|
||||
+ ", executions="
|
||||
+ this.executions
|
||||
+ "}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.states;
|
||||
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.component.Component;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public class FarmingBlockState implements Component<ChunkStore> {
|
||||
@Nonnull
|
||||
public static BuilderCodec<FarmingBlockState> CODEC = BuilderCodec.builder(FarmingBlockState.class, FarmingBlockState::new)
|
||||
.append(new KeyedCodec<>("BaseCrop", Codec.STRING), (state, crop) -> state.baseCrop = crop, state -> state.baseCrop)
|
||||
.add()
|
||||
.append(new KeyedCodec<>("StageStart", Codec.INSTANT), (state, start) -> state.stageStart = start, state -> state.stageStart)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("CurrentFarmingStageIndex", Codec.INTEGER),
|
||||
(baseFarmingBlockState, integer) -> baseFarmingBlockState.currentFarmingStageIndex = integer,
|
||||
baseFarmingBlockState -> baseFarmingBlockState.currentFarmingStageIndex
|
||||
)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("CurrentFarmingStageSetName", Codec.STRING),
|
||||
(farmingBlockState, s) -> farmingBlockState.currentFarmingStageSetName = s,
|
||||
farmingBlockState -> farmingBlockState.currentFarmingStageSetName
|
||||
)
|
||||
.add()
|
||||
.append(new KeyedCodec<>("SpreadRate", Codec.FLOAT), (blockState, aFloat) -> blockState.spreadRate = aFloat, blockState -> blockState.spreadRate)
|
||||
.add()
|
||||
.build();
|
||||
public boolean loaded;
|
||||
public String baseCrop;
|
||||
public Instant stageStart;
|
||||
public String currentFarmingStageSetName;
|
||||
public int currentFarmingStageIndex;
|
||||
public Instant[] stageCompletionTimes;
|
||||
public String stageSetAfterHarvest;
|
||||
public double lastGrowthMultiplier;
|
||||
public float spreadRate = 1.0F;
|
||||
|
||||
public FarmingBlockState() {
|
||||
}
|
||||
|
||||
public String getCurrentFarmingStageSetName() {
|
||||
return this.currentFarmingStageSetName;
|
||||
}
|
||||
|
||||
public void setCurrentFarmingStageSetName(String currentFarmingStageSetName) {
|
||||
this.currentFarmingStageSetName = currentFarmingStageSetName;
|
||||
}
|
||||
|
||||
public int getCurrentFarmingStageIndex() {
|
||||
return this.currentFarmingStageIndex;
|
||||
}
|
||||
|
||||
public void setCurrentFarmingStageIndex(int currentFarmingStageIndex) {
|
||||
this.currentFarmingStageIndex = currentFarmingStageIndex;
|
||||
}
|
||||
|
||||
public String getStageSetAfterHarvest() {
|
||||
return this.stageSetAfterHarvest;
|
||||
}
|
||||
|
||||
public void setStageSetAfterHarvest(String stageSetAfterHarvest) {
|
||||
this.stageSetAfterHarvest = stageSetAfterHarvest;
|
||||
}
|
||||
|
||||
public float getSpreadRate() {
|
||||
return this.spreadRate;
|
||||
}
|
||||
|
||||
public void setSpreadRate(float spreadRate) {
|
||||
this.spreadRate = spreadRate;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FarmingBlockState{loaded="
|
||||
+ this.loaded
|
||||
+ ", baseCrop="
|
||||
+ this.baseCrop
|
||||
+ ", stageStart="
|
||||
+ this.stageStart
|
||||
+ ", currentFarmingStageSetName='"
|
||||
+ this.currentFarmingStageSetName
|
||||
+ "', currentFarmingStageIndex="
|
||||
+ this.currentFarmingStageIndex
|
||||
+ ", stageCompletionTimes="
|
||||
+ Arrays.toString((Object[])this.stageCompletionTimes)
|
||||
+ ", stageSetAfterHarvest='"
|
||||
+ this.stageSetAfterHarvest
|
||||
+ "', lastGrowthMultiplier="
|
||||
+ this.lastGrowthMultiplier
|
||||
+ "} "
|
||||
+ super.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Component<ChunkStore> clone() {
|
||||
return this;
|
||||
}
|
||||
|
||||
protected static class RefreshFlags {
|
||||
protected static final int REFRESH_ALL_FLAG = 1;
|
||||
protected static final int UNLOADING_FLAG = 2;
|
||||
protected static final int RETROACTIVE_FLAG = 4;
|
||||
protected static final int DEFAULT = 1;
|
||||
protected static final int ON_UNLOADING = 3;
|
||||
protected static final int ON_LOADING = 5;
|
||||
protected static final int NONE = 0;
|
||||
|
||||
protected RefreshFlags() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.hypixel.hytale.builtin.adventure.farming.states;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.farming.FarmingPlugin;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.map.MapCodec;
|
||||
import com.hypixel.hytale.common.util.ArrayUtil;
|
||||
import com.hypixel.hytale.component.Component;
|
||||
import com.hypixel.hytale.component.ComponentType;
|
||||
import com.hypixel.hytale.server.core.asset.type.blocktype.config.BlockType;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.ChunkStore;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import java.time.Instant;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class TilledSoilBlock implements Component<ChunkStore> {
|
||||
public static int VERSION = 1;
|
||||
public static final BuilderCodec<TilledSoilBlock> CODEC = BuilderCodec.builder(TilledSoilBlock.class, TilledSoilBlock::new)
|
||||
.versioned()
|
||||
.codecVersion(VERSION)
|
||||
.append(new KeyedCodec<>("Planted", Codec.BOOLEAN), (state, planted) -> state.planted = planted, state -> state.planted ? Boolean.TRUE : null)
|
||||
.add()
|
||||
.<Map>append(new KeyedCodec<>("ModifierTimes", new MapCodec<>(Codec.INSTANT, Object2ObjectOpenHashMap::new, false)), (state, times) -> {
|
||||
if (times != null) {
|
||||
state.wateredUntil = (Instant)times.get("WateredUntil");
|
||||
}
|
||||
}, state -> null)
|
||||
.setVersionRange(0, 0)
|
||||
.add()
|
||||
.<String[]>append(new KeyedCodec<>("Flags", Codec.STRING_ARRAY), (state, flags) -> {
|
||||
if (flags != null) {
|
||||
state.fertilized = ArrayUtil.contains(flags, "Fertilized");
|
||||
state.externalWater = ArrayUtil.contains(flags, "ExternalWater");
|
||||
}
|
||||
}, state -> null)
|
||||
.setVersionRange(0, 0)
|
||||
.add()
|
||||
.<Boolean>append(new KeyedCodec<>("Fertilized", Codec.BOOLEAN), (state, v) -> state.fertilized = v, state -> state.fertilized ? Boolean.TRUE : null)
|
||||
.setVersionRange(1, VERSION)
|
||||
.add()
|
||||
.<Boolean>append(
|
||||
new KeyedCodec<>("ExternalWater", Codec.BOOLEAN), (state, v) -> state.externalWater = v, state -> state.externalWater ? Boolean.TRUE : null
|
||||
)
|
||||
.setVersionRange(1, VERSION)
|
||||
.add()
|
||||
.<Instant>append(new KeyedCodec<>("WateredUntil", Codec.INSTANT), (state, v) -> state.wateredUntil = v, state -> state.wateredUntil)
|
||||
.setVersionRange(1, VERSION)
|
||||
.add()
|
||||
.append(new KeyedCodec<>("DecayTime", Codec.INSTANT), (state, v) -> state.decayTime = v, state -> state.decayTime)
|
||||
.add()
|
||||
.build();
|
||||
protected boolean planted;
|
||||
protected boolean fertilized;
|
||||
protected boolean externalWater;
|
||||
@Nullable
|
||||
protected Instant wateredUntil;
|
||||
@Nullable
|
||||
protected Instant decayTime;
|
||||
|
||||
public static ComponentType<ChunkStore, TilledSoilBlock> getComponentType() {
|
||||
return FarmingPlugin.get().getTiledSoilBlockComponentType();
|
||||
}
|
||||
|
||||
public TilledSoilBlock() {
|
||||
}
|
||||
|
||||
public TilledSoilBlock(boolean planted, boolean fertilized, boolean externalWater, Instant wateredUntil, Instant decayTime) {
|
||||
this.planted = planted;
|
||||
this.fertilized = fertilized;
|
||||
this.externalWater = externalWater;
|
||||
this.wateredUntil = wateredUntil;
|
||||
this.decayTime = decayTime;
|
||||
}
|
||||
|
||||
public boolean isPlanted() {
|
||||
return this.planted;
|
||||
}
|
||||
|
||||
public void setPlanted(boolean planted) {
|
||||
this.planted = planted;
|
||||
}
|
||||
|
||||
public void setWateredUntil(@Nullable Instant wateredUntil) {
|
||||
this.wateredUntil = wateredUntil;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Instant getWateredUntil() {
|
||||
return this.wateredUntil;
|
||||
}
|
||||
|
||||
public boolean isFertilized() {
|
||||
return this.fertilized;
|
||||
}
|
||||
|
||||
public void setFertilized(boolean fertilized) {
|
||||
this.fertilized = fertilized;
|
||||
}
|
||||
|
||||
public boolean hasExternalWater() {
|
||||
return this.externalWater;
|
||||
}
|
||||
|
||||
public void setExternalWater(boolean externalWater) {
|
||||
this.externalWater = externalWater;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Instant getDecayTime() {
|
||||
return this.decayTime;
|
||||
}
|
||||
|
||||
public void setDecayTime(@Nullable Instant decayTime) {
|
||||
this.decayTime = decayTime;
|
||||
}
|
||||
|
||||
public String computeBlockType(Instant gameTime, BlockType type) {
|
||||
boolean watered = this.hasExternalWater() || this.wateredUntil != null && this.wateredUntil.isAfter(gameTime);
|
||||
if (this.fertilized && watered) {
|
||||
return type.getBlockKeyForState("Fertilized_Watered");
|
||||
} else if (this.fertilized) {
|
||||
return type.getBlockKeyForState("Fertilized");
|
||||
} else {
|
||||
return watered ? type.getBlockKeyForState("Watered") : type.getBlockKeyForState("default");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TilledSoilBlock{planted="
|
||||
+ this.planted
|
||||
+ ", fertilized="
|
||||
+ this.fertilized
|
||||
+ ", externalWater="
|
||||
+ this.externalWater
|
||||
+ ", wateredUntil="
|
||||
+ this.wateredUntil
|
||||
+ ", decayTime="
|
||||
+ this.decayTime
|
||||
+ "}";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Component<ChunkStore> clone() {
|
||||
return new TilledSoilBlock(this.planted, this.fertilized, this.externalWater, this.wateredUntil, this.decayTime);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories;
|
||||
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig;
|
||||
import com.hypixel.hytale.server.core.asset.type.item.config.Item;
|
||||
import com.hypixel.hytale.server.core.asset.type.model.config.ModelParticle;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class MemoriesGameplayConfig {
|
||||
public static final String ID = "Memories";
|
||||
public static final BuilderCodec<MemoriesGameplayConfig> CODEC = BuilderCodec.builder(MemoriesGameplayConfig.class, MemoriesGameplayConfig::new)
|
||||
.appendInherited(
|
||||
new KeyedCodec<>("MemoriesAmountPerLevel", Codec.INT_ARRAY),
|
||||
(config, value) -> config.memoriesAmountPerLevel = value,
|
||||
config -> config.memoriesAmountPerLevel,
|
||||
(config, parent) -> config.memoriesAmountPerLevel = parent.memoriesAmountPerLevel
|
||||
)
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.<String>appendInherited(
|
||||
new KeyedCodec<>("MemoriesRecordParticles", Codec.STRING),
|
||||
(config, value) -> config.memoriesRecordParticles = value,
|
||||
config -> config.memoriesRecordParticles,
|
||||
(config, parent) -> config.memoriesRecordParticles = parent.memoriesRecordParticles
|
||||
)
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.<String>appendInherited(
|
||||
new KeyedCodec<>("MemoriesCatchItemId", Codec.STRING),
|
||||
(memoriesGameplayConfig, s) -> memoriesGameplayConfig.memoriesCatchItemId = s,
|
||||
memoriesGameplayConfig -> memoriesGameplayConfig.memoriesCatchItemId,
|
||||
(memoriesGameplayConfig, parent) -> memoriesGameplayConfig.memoriesCatchItemId = parent.memoriesCatchItemId
|
||||
)
|
||||
.addValidator(Validators.nonNull())
|
||||
.addValidator(Item.VALIDATOR_CACHE.getValidator())
|
||||
.add()
|
||||
.<ModelParticle>appendInherited(
|
||||
new KeyedCodec<>("MemoriesCatchEntityParticle", ModelParticle.CODEC),
|
||||
(activationEffects, s) -> activationEffects.memoriesCatchEntityParticle = s,
|
||||
activationEffects -> activationEffects.memoriesCatchEntityParticle,
|
||||
(activationEffects, parent) -> activationEffects.memoriesCatchEntityParticle = parent.memoriesCatchEntityParticle
|
||||
)
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.<Integer>appendInherited(
|
||||
new KeyedCodec<>("MemoriesCatchParticleViewDistance", Codec.INTEGER),
|
||||
(memoriesGameplayConfig, integer) -> memoriesGameplayConfig.memoriesCatchParticleViewDistance = integer,
|
||||
memoriesGameplayConfig -> memoriesGameplayConfig.memoriesCatchParticleViewDistance,
|
||||
(memoriesGameplayConfig, parent) -> memoriesGameplayConfig.memoriesCatchParticleViewDistance = parent.memoriesCatchParticleViewDistance
|
||||
)
|
||||
.addValidator(Validators.greaterThan(16))
|
||||
.add()
|
||||
.build();
|
||||
private int[] memoriesAmountPerLevel;
|
||||
private String memoriesRecordParticles;
|
||||
private String memoriesCatchItemId;
|
||||
private ModelParticle memoriesCatchEntityParticle;
|
||||
private int memoriesCatchParticleViewDistance = 64;
|
||||
|
||||
public MemoriesGameplayConfig() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static MemoriesGameplayConfig get(@Nonnull GameplayConfig config) {
|
||||
return config.getPluginConfig().get(MemoriesGameplayConfig.class);
|
||||
}
|
||||
|
||||
public int[] getMemoriesAmountPerLevel() {
|
||||
return this.memoriesAmountPerLevel;
|
||||
}
|
||||
|
||||
public String getMemoriesRecordParticles() {
|
||||
return this.memoriesRecordParticles;
|
||||
}
|
||||
|
||||
public String getMemoriesCatchItemId() {
|
||||
return this.memoriesCatchItemId;
|
||||
}
|
||||
|
||||
public ModelParticle getMemoriesCatchEntityParticle() {
|
||||
return this.memoriesCatchEntityParticle;
|
||||
}
|
||||
|
||||
public int getMemoriesCatchParticleViewDistance() {
|
||||
return this.memoriesCatchParticleViewDistance;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,386 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.memories.commands.MemoriesCommand;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.component.PlayerMemories;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.interactions.MemoriesConditionInteraction;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.interactions.SetMemoriesCapacityInteraction;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.memories.Memory;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.memories.MemoryProvider;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.memories.npc.NPCMemory;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.memories.npc.NPCMemoryProvider;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.page.MemoriesPage;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.page.MemoriesPageSupplier;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.page.MemoriesUnlockedPage;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.page.MemoriesUnlockedPageSuplier;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.temple.ForgottenTempleConfig;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.temple.TempleRespawnPlayersSystem;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.window.MemoriesWindow;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
|
||||
import com.hypixel.hytale.codec.codecs.map.Object2DoubleMapCodec;
|
||||
import com.hypixel.hytale.codec.util.RawJsonReader;
|
||||
import com.hypixel.hytale.component.AddReason;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.ComponentRegistryProxy;
|
||||
import com.hypixel.hytale.component.ComponentType;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.RemoveReason;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.component.dependency.Dependency;
|
||||
import com.hypixel.hytale.component.dependency.Order;
|
||||
import com.hypixel.hytale.component.dependency.SystemDependency;
|
||||
import com.hypixel.hytale.component.query.Query;
|
||||
import com.hypixel.hytale.component.system.RefSystem;
|
||||
import com.hypixel.hytale.protocol.packets.player.UpdateMemoriesFeatureStatus;
|
||||
import com.hypixel.hytale.protocol.packets.window.WindowType;
|
||||
import com.hypixel.hytale.server.core.Constants;
|
||||
import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig;
|
||||
import com.hypixel.hytale.server.core.entity.entities.Player;
|
||||
import com.hypixel.hytale.server.core.entity.entities.player.windows.Window;
|
||||
import com.hypixel.hytale.server.core.io.PacketHandler;
|
||||
import com.hypixel.hytale.server.core.modules.entity.player.PlayerSystems;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.OpenCustomUIInteraction;
|
||||
import com.hypixel.hytale.server.core.plugin.JavaPlugin;
|
||||
import com.hypixel.hytale.server.core.plugin.JavaPluginInit;
|
||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import com.hypixel.hytale.server.core.util.BsonUtil;
|
||||
import com.hypixel.hytale.server.core.util.Config;
|
||||
import com.hypixel.hytale.server.npc.AllNPCsLoadedEvent;
|
||||
import it.unimi.dsi.fastutil.objects.Object2DoubleMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2DoubleMaps;
|
||||
import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectRBTreeMap;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class MemoriesPlugin extends JavaPlugin {
|
||||
private static MemoriesPlugin instance;
|
||||
private final Config<MemoriesPlugin.MemoriesPluginConfig> config = this.withConfig(MemoriesPlugin.MemoriesPluginConfig.CODEC);
|
||||
private final List<MemoryProvider<?>> providers = new ObjectArrayList<>();
|
||||
private final Map<String, Set<Memory>> allMemories = new Object2ObjectRBTreeMap<>();
|
||||
private ComponentType<EntityStore, PlayerMemories> playerMemoriesComponentType;
|
||||
@Nullable
|
||||
private MemoriesPlugin.RecordedMemories recordedMemories;
|
||||
private boolean hasInitializedMemories;
|
||||
|
||||
public static MemoriesPlugin get() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public MemoriesPlugin(@Nonnull JavaPluginInit init) {
|
||||
super(init);
|
||||
instance = this;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setup() {
|
||||
ComponentRegistryProxy<EntityStore> entityStoreRegistry = this.getEntityStoreRegistry();
|
||||
this.getCommandRegistry().registerCommand(new MemoriesCommand());
|
||||
OpenCustomUIInteraction.registerCustomPageSupplier(this, MemoriesPage.class, "Memories", new MemoriesPageSupplier());
|
||||
OpenCustomUIInteraction.registerCustomPageSupplier(this, MemoriesUnlockedPage.class, "MemoriesUnlocked", new MemoriesUnlockedPageSuplier());
|
||||
Window.CLIENT_REQUESTABLE_WINDOW_TYPES.put(WindowType.Memories, MemoriesWindow::new);
|
||||
this.playerMemoriesComponentType = entityStoreRegistry.registerComponent(PlayerMemories.class, "PlayerMemories", PlayerMemories.CODEC);
|
||||
NPCMemoryProvider npcMemoryProvider = new NPCMemoryProvider();
|
||||
this.registerMemoryProvider(npcMemoryProvider);
|
||||
entityStoreRegistry.registerSystem(new NPCMemory.GatherMemoriesSystem(npcMemoryProvider.getCollectionRadius()));
|
||||
|
||||
for (MemoryProvider<?> provider : this.providers) {
|
||||
BuilderCodec<? extends Memory> codec = (BuilderCodec<? extends Memory>)provider.getCodec();
|
||||
this.getCodecRegistry(Memory.CODEC).register(provider.getId(), codec.getInnerClass(), codec);
|
||||
}
|
||||
|
||||
this.getEventRegistry().register(AllNPCsLoadedEvent.class, event -> this.onAssetsLoad());
|
||||
entityStoreRegistry.registerSystem(new MemoriesPlugin.PlayerAddedSystem());
|
||||
this.getCodecRegistry(Interaction.CODEC).register("SetMemoriesCapacity", SetMemoriesCapacityInteraction.class, SetMemoriesCapacityInteraction.CODEC);
|
||||
this.getCodecRegistry(GameplayConfig.PLUGIN_CODEC).register(MemoriesGameplayConfig.class, "Memories", MemoriesGameplayConfig.CODEC);
|
||||
this.getCodecRegistry(Interaction.CODEC).register("MemoriesCondition", MemoriesConditionInteraction.class, MemoriesConditionInteraction.CODEC);
|
||||
entityStoreRegistry.registerSystem(new TempleRespawnPlayersSystem());
|
||||
this.getCodecRegistry(GameplayConfig.PLUGIN_CODEC).register(ForgottenTempleConfig.class, "ForgottenTemple", ForgottenTempleConfig.CODEC);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void start() {
|
||||
try {
|
||||
Path path = Constants.UNIVERSE_PATH.resolve("memories.json");
|
||||
if (Files.exists(path)) {
|
||||
this.recordedMemories = RawJsonReader.readSync(path, MemoriesPlugin.RecordedMemories.CODEC, this.getLogger());
|
||||
} else {
|
||||
this.recordedMemories = new MemoriesPlugin.RecordedMemories();
|
||||
}
|
||||
} catch (IOException var2) {
|
||||
throw new RuntimeException(var2);
|
||||
}
|
||||
|
||||
this.hasInitializedMemories = true;
|
||||
this.onAssetsLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutdown() {
|
||||
if (this.hasInitializedMemories) {
|
||||
this.recordedMemories.lock.readLock().lock();
|
||||
|
||||
try {
|
||||
BsonUtil.writeSync(Constants.UNIVERSE_PATH.resolve("memories.json"), MemoriesPlugin.RecordedMemories.CODEC, this.recordedMemories, this.getLogger());
|
||||
} catch (IOException var5) {
|
||||
throw new RuntimeException(var5);
|
||||
} finally {
|
||||
this.recordedMemories.lock.readLock().unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onAssetsLoad() {
|
||||
if (this.hasInitializedMemories) {
|
||||
this.allMemories.clear();
|
||||
|
||||
for (MemoryProvider<?> provider : this.providers) {
|
||||
for (Entry<String, Set<Memory>> entry : provider.getAllMemories().entrySet()) {
|
||||
this.allMemories.computeIfAbsent(entry.getKey(), k -> new HashSet<>()).addAll(entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MemoriesPlugin.MemoriesPluginConfig getConfig() {
|
||||
return this.config.get();
|
||||
}
|
||||
|
||||
public ComponentType<EntityStore, PlayerMemories> getPlayerMemoriesComponentType() {
|
||||
return this.playerMemoriesComponentType;
|
||||
}
|
||||
|
||||
public <T extends Memory> void registerMemoryProvider(MemoryProvider<T> memoryProvider) {
|
||||
this.providers.add(memoryProvider);
|
||||
}
|
||||
|
||||
public Map<String, Set<Memory>> getAllMemories() {
|
||||
return this.allMemories;
|
||||
}
|
||||
|
||||
public int getMemoriesLevel(@Nonnull GameplayConfig gameplayConfig) {
|
||||
MemoriesGameplayConfig config = MemoriesGameplayConfig.get(gameplayConfig);
|
||||
int memoriesLevel = 1;
|
||||
if (config == null) {
|
||||
return memoriesLevel;
|
||||
} else {
|
||||
int recordedMemoriesCount = this.getRecordedMemories().size();
|
||||
int[] memoriesAmountPerLevel = config.getMemoriesAmountPerLevel();
|
||||
|
||||
for (int i = memoriesAmountPerLevel.length - 1; i >= 0; i--) {
|
||||
if (recordedMemoriesCount >= memoriesAmountPerLevel[i]) {
|
||||
return i + 2;
|
||||
}
|
||||
}
|
||||
|
||||
return memoriesLevel;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasRecordedMemory(Memory memory) {
|
||||
this.recordedMemories.lock.readLock().lock();
|
||||
|
||||
boolean var2;
|
||||
try {
|
||||
var2 = this.recordedMemories.memories.contains(memory);
|
||||
} finally {
|
||||
this.recordedMemories.lock.readLock().unlock();
|
||||
}
|
||||
|
||||
return var2;
|
||||
}
|
||||
|
||||
public boolean recordPlayerMemories(@Nonnull PlayerMemories playerMemories) {
|
||||
this.recordedMemories.lock.writeLock().lock();
|
||||
|
||||
try {
|
||||
if (playerMemories.takeMemories(this.recordedMemories.memories)) {
|
||||
BsonUtil.writeSync(Constants.UNIVERSE_PATH.resolve("memories.json"), MemoriesPlugin.RecordedMemories.CODEC, this.recordedMemories, this.getLogger());
|
||||
return true;
|
||||
}
|
||||
} catch (IOException var6) {
|
||||
throw new RuntimeException(var6);
|
||||
} finally {
|
||||
this.recordedMemories.lock.writeLock().unlock();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Set<Memory> getRecordedMemories() {
|
||||
this.recordedMemories.lock.readLock().lock();
|
||||
|
||||
HashSet var1;
|
||||
try {
|
||||
var1 = new HashSet<>(this.recordedMemories.memories);
|
||||
} finally {
|
||||
this.recordedMemories.lock.readLock().unlock();
|
||||
}
|
||||
|
||||
return var1;
|
||||
}
|
||||
|
||||
public void clearRecordedMemories() {
|
||||
this.recordedMemories.lock.writeLock().lock();
|
||||
|
||||
try {
|
||||
this.recordedMemories.memories.clear();
|
||||
BsonUtil.writeSync(Constants.UNIVERSE_PATH.resolve("memories.json"), MemoriesPlugin.RecordedMemories.CODEC, this.recordedMemories, this.getLogger());
|
||||
} catch (IOException var5) {
|
||||
throw new RuntimeException(var5);
|
||||
} finally {
|
||||
this.recordedMemories.lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void recordAllMemories() {
|
||||
this.recordedMemories.lock.writeLock().lock();
|
||||
|
||||
try {
|
||||
for (Entry<String, Set<Memory>> entry : this.allMemories.entrySet()) {
|
||||
this.recordedMemories.memories.addAll(entry.getValue());
|
||||
}
|
||||
|
||||
BsonUtil.writeSync(Constants.UNIVERSE_PATH.resolve("memories.json"), MemoriesPlugin.RecordedMemories.CODEC, this.recordedMemories, this.getLogger());
|
||||
} catch (IOException var6) {
|
||||
throw new RuntimeException(var6);
|
||||
} finally {
|
||||
this.recordedMemories.lock.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int setRecordedMemoriesCount(int count) {
|
||||
if (count < 0) {
|
||||
count = 0;
|
||||
}
|
||||
|
||||
this.recordedMemories.lock.writeLock().lock();
|
||||
|
||||
int var12;
|
||||
try {
|
||||
this.recordedMemories.memories.clear();
|
||||
List<Memory> allAvailableMemories = new ObjectArrayList<>();
|
||||
|
||||
for (Entry<String, Set<Memory>> entry : this.allMemories.entrySet()) {
|
||||
allAvailableMemories.addAll(entry.getValue());
|
||||
}
|
||||
|
||||
int actualCount = Math.min(count, allAvailableMemories.size());
|
||||
|
||||
for (int i = 0; i < actualCount; i++) {
|
||||
this.recordedMemories.memories.add(allAvailableMemories.get(i));
|
||||
}
|
||||
|
||||
BsonUtil.writeSync(Constants.UNIVERSE_PATH.resolve("memories.json"), MemoriesPlugin.RecordedMemories.CODEC, this.recordedMemories, this.getLogger());
|
||||
var12 = actualCount;
|
||||
} catch (IOException var8) {
|
||||
throw new RuntimeException(var8);
|
||||
} finally {
|
||||
this.recordedMemories.lock.writeLock().unlock();
|
||||
}
|
||||
|
||||
return var12;
|
||||
}
|
||||
|
||||
public static class MemoriesPluginConfig {
|
||||
public static final BuilderCodec<MemoriesPlugin.MemoriesPluginConfig> CODEC = BuilderCodec.builder(
|
||||
MemoriesPlugin.MemoriesPluginConfig.class, MemoriesPlugin.MemoriesPluginConfig::new
|
||||
)
|
||||
.append(
|
||||
new KeyedCodec<>("CollectionRadius", new Object2DoubleMapCodec<>(Codec.STRING, Object2DoubleOpenHashMap::new)),
|
||||
(config, map) -> config.collectionRadius = map,
|
||||
config -> config.collectionRadius
|
||||
)
|
||||
.add()
|
||||
.build();
|
||||
private Object2DoubleMap<String> collectionRadius;
|
||||
|
||||
public MemoriesPluginConfig() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Object2DoubleMap<String> getCollectionRadius() {
|
||||
return (Object2DoubleMap<String>)(this.collectionRadius != null ? this.collectionRadius : Object2DoubleMaps.EMPTY_MAP);
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlayerAddedSystem extends RefSystem<EntityStore> {
|
||||
@Nonnull
|
||||
private final Set<Dependency<EntityStore>> dependencies = Set.of(new SystemDependency<>(Order.AFTER, PlayerSystems.PlayerSpawnedSystem.class));
|
||||
@Nonnull
|
||||
private final Query<EntityStore> query = Query.and(Player.getComponentType(), PlayerRef.getComponentType());
|
||||
|
||||
public PlayerAddedSystem() {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Query<EntityStore> getQuery() {
|
||||
return this.query;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<Dependency<EntityStore>> getDependencies() {
|
||||
return this.dependencies;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityAdded(
|
||||
@Nonnull Ref<EntityStore> ref, @Nonnull AddReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer
|
||||
) {
|
||||
Player playerComponent = store.getComponent(ref, Player.getComponentType());
|
||||
|
||||
assert playerComponent != null;
|
||||
|
||||
PlayerRef playerRefComponent = store.getComponent(ref, PlayerRef.getComponentType());
|
||||
|
||||
assert playerRefComponent != null;
|
||||
|
||||
PlayerMemories playerMemoriesComponent = store.getComponent(ref, PlayerMemories.getComponentType());
|
||||
boolean isFeatureUnlockedByPlayer = playerMemoriesComponent != null;
|
||||
PacketHandler playerConnection = playerRefComponent.getPacketHandler();
|
||||
playerConnection.writeNoCache(new UpdateMemoriesFeatureStatus(isFeatureUnlockedByPlayer));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEntityRemove(
|
||||
@Nonnull Ref<EntityStore> ref, @Nonnull RemoveReason reason, @Nonnull Store<EntityStore> store, @Nonnull CommandBuffer<EntityStore> commandBuffer
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
private static class RecordedMemories {
|
||||
public static final BuilderCodec<MemoriesPlugin.RecordedMemories> CODEC = BuilderCodec.builder(
|
||||
MemoriesPlugin.RecordedMemories.class, MemoriesPlugin.RecordedMemories::new
|
||||
)
|
||||
.append(new KeyedCodec<>("Memories", new ArrayCodec<>(Memory.CODEC, Memory[]::new)), (recordedMemories, memories) -> {
|
||||
if (memories != null) {
|
||||
Collections.addAll(recordedMemories.memories, memories);
|
||||
}
|
||||
}, recordedMemories -> recordedMemories.memories.toArray(Memory[]::new))
|
||||
.add()
|
||||
.build();
|
||||
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
|
||||
private final Set<Memory> memories = new HashSet<>();
|
||||
|
||||
private RecordedMemories() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.commands;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.memories.component.PlayerMemories;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.protocol.packets.player.UpdateMemoriesFeatureStatus;
|
||||
import com.hypixel.hytale.server.core.Message;
|
||||
import com.hypixel.hytale.server.core.command.system.CommandContext;
|
||||
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
|
||||
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
|
||||
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand;
|
||||
import com.hypixel.hytale.server.core.io.PacketHandler;
|
||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class MemoriesCapacityCommand extends AbstractPlayerCommand {
|
||||
@Nonnull
|
||||
private final RequiredArg<Integer> capacityArg = this.withRequiredArg("capacity", "server.commands.memories.capacity.capacity.desc", ArgTypes.INTEGER);
|
||||
|
||||
public MemoriesCapacityCommand() {
|
||||
super("capacity", "server.commands.memories.capacity.desc");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(
|
||||
@Nonnull CommandContext context, @Nonnull Store<EntityStore> store, @Nonnull Ref<EntityStore> ref, @Nonnull PlayerRef playerRef, @Nonnull World world
|
||||
) {
|
||||
Integer capacity = this.capacityArg.get(context);
|
||||
PacketHandler playerConnection = playerRef.getPacketHandler();
|
||||
if (capacity <= 0) {
|
||||
store.tryRemoveComponent(ref, PlayerMemories.getComponentType());
|
||||
context.sendMessage(Message.translation("server.commands.memories.capacity.success").param("capacity", 0));
|
||||
playerConnection.writeNoCache(new UpdateMemoriesFeatureStatus(false));
|
||||
} else {
|
||||
PlayerMemories playerMemories = store.ensureAndGetComponent(ref, PlayerMemories.getComponentType());
|
||||
playerMemories.setMemoriesCapacity(capacity);
|
||||
context.sendMessage(Message.translation("server.commands.memories.capacity.success").param("capacity", capacity));
|
||||
playerConnection.writeNoCache(new UpdateMemoriesFeatureStatus(true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.commands;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.memories.MemoriesPlugin;
|
||||
import com.hypixel.hytale.server.core.Message;
|
||||
import com.hypixel.hytale.server.core.command.system.CommandContext;
|
||||
import com.hypixel.hytale.server.core.command.system.basecommands.CommandBase;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class MemoriesClearCommand extends CommandBase {
|
||||
@Nonnull
|
||||
private static final Message MESSAGE_COMMANDS_MEMORIES_CLEAR_SUCCESS = Message.translation("server.commands.memories.clear.success");
|
||||
|
||||
public MemoriesClearCommand() {
|
||||
super("clear", "server.commands.memories.clear.desc");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void executeSync(@Nonnull CommandContext context) {
|
||||
MemoriesPlugin.get().clearRecordedMemories();
|
||||
context.sendMessage(MESSAGE_COMMANDS_MEMORIES_CLEAR_SUCCESS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.commands;
|
||||
|
||||
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractCommandCollection;
|
||||
|
||||
public class MemoriesCommand extends AbstractCommandCollection {
|
||||
public MemoriesCommand() {
|
||||
super("memories", "server.commands.memories.desc");
|
||||
this.addSubCommand(new MemoriesClearCommand());
|
||||
this.addSubCommand(new MemoriesCapacityCommand());
|
||||
this.addSubCommand(new MemoriesLevelCommand());
|
||||
this.addSubCommand(new MemoriesUnlockCommand());
|
||||
this.addSubCommand(new MemoriesSetCountCommand());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.commands;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.memories.MemoriesPlugin;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.server.core.Message;
|
||||
import com.hypixel.hytale.server.core.command.system.CommandContext;
|
||||
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractWorldCommand;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class MemoriesLevelCommand extends AbstractWorldCommand {
|
||||
public MemoriesLevelCommand() {
|
||||
super("level", "server.commands.memories.level.desc");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@Nonnull CommandContext context, @Nonnull World world, @Nonnull Store<EntityStore> store) {
|
||||
int level = MemoriesPlugin.get().getMemoriesLevel(world.getGameplayConfig());
|
||||
context.sendMessage(Message.translation("server.commands.memories.level.success").param("level", level));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.commands;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.memories.MemoriesPlugin;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.server.core.Message;
|
||||
import com.hypixel.hytale.server.core.command.system.CommandContext;
|
||||
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
|
||||
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
|
||||
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractWorldCommand;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class MemoriesSetCountCommand extends AbstractWorldCommand {
|
||||
@Nonnull
|
||||
private static final Message MESSAGE_COMMANDS_MEMORIES_SETCOUNT_INVALID = Message.translation("server.commands.memories.setCount.invalid");
|
||||
@Nonnull
|
||||
private final RequiredArg<Integer> countArg = this.withRequiredArg("count", "server.commands.memories.setCount.count.desc", ArgTypes.INTEGER);
|
||||
|
||||
public MemoriesSetCountCommand() {
|
||||
super("setCount", "server.commands.memories.setCount.desc");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@Nonnull CommandContext context, @Nonnull World world, @Nonnull Store<EntityStore> store) {
|
||||
int count = this.countArg.get(context);
|
||||
if (count < 0) {
|
||||
context.sendMessage(MESSAGE_COMMANDS_MEMORIES_SETCOUNT_INVALID);
|
||||
} else {
|
||||
int actualCount = MemoriesPlugin.get().setRecordedMemoriesCount(count);
|
||||
context.sendMessage(Message.translation("server.commands.memories.setCount.success").param("requested", count).param("actual", actualCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.commands;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.memories.MemoriesPlugin;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.server.core.Message;
|
||||
import com.hypixel.hytale.server.core.command.system.CommandContext;
|
||||
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractWorldCommand;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class MemoriesUnlockCommand extends AbstractWorldCommand {
|
||||
@Nonnull
|
||||
private static final Message MESSAGE_COMMANDS_MEMORIES_UNLOCK_ALL_SUCCESS = Message.translation("server.commands.memories.unlockAll.success");
|
||||
|
||||
public MemoriesUnlockCommand() {
|
||||
super("unlockAll", "server.commands.memories.unlockAll.desc");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(@Nonnull CommandContext context, @Nonnull World world, @Nonnull Store<EntityStore> store) {
|
||||
MemoriesPlugin.get().recordAllMemories();
|
||||
context.sendMessage(MESSAGE_COMMANDS_MEMORIES_UNLOCK_ALL_SUCCESS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.component;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.memories.MemoriesPlugin;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.memories.Memory;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.array.ArrayCodec;
|
||||
import com.hypixel.hytale.component.Component;
|
||||
import com.hypixel.hytale.component.ComponentType;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class PlayerMemories implements Component<EntityStore> {
|
||||
public static final BuilderCodec<PlayerMemories> CODEC = BuilderCodec.builder(PlayerMemories.class, PlayerMemories::new)
|
||||
.append(
|
||||
new KeyedCodec<>("Capacity", Codec.INTEGER),
|
||||
(playerMemories, integer) -> playerMemories.memoriesCapacity = integer,
|
||||
playerMemories -> playerMemories.memoriesCapacity
|
||||
)
|
||||
.add()
|
||||
.append(new KeyedCodec<>("Memories", new ArrayCodec<>(Memory.CODEC, Memory[]::new)), (playerMemories, memories) -> {
|
||||
if (memories != null) {
|
||||
Collections.addAll(playerMemories.memories, memories);
|
||||
}
|
||||
}, playerMemories -> playerMemories.memories.toArray(Memory[]::new))
|
||||
.add()
|
||||
.build();
|
||||
private final Set<Memory> memories = new LinkedHashSet<>();
|
||||
private int memoriesCapacity;
|
||||
|
||||
public PlayerMemories() {
|
||||
}
|
||||
|
||||
public static ComponentType<EntityStore, PlayerMemories> getComponentType() {
|
||||
return MemoriesPlugin.get().getPlayerMemoriesComponentType();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Component<EntityStore> clone() {
|
||||
PlayerMemories playerMemories = new PlayerMemories();
|
||||
playerMemories.memories.addAll(this.memories);
|
||||
playerMemories.memoriesCapacity = this.memoriesCapacity;
|
||||
return playerMemories;
|
||||
}
|
||||
|
||||
public int getMemoriesCapacity() {
|
||||
return this.memoriesCapacity;
|
||||
}
|
||||
|
||||
public void setMemoriesCapacity(int memoriesCapacity) {
|
||||
this.memoriesCapacity = memoriesCapacity;
|
||||
}
|
||||
|
||||
public boolean recordMemory(Memory memory) {
|
||||
return this.memories.size() >= this.memoriesCapacity ? false : this.memories.add(memory);
|
||||
}
|
||||
|
||||
public boolean hasMemories() {
|
||||
return !this.memories.isEmpty();
|
||||
}
|
||||
|
||||
public boolean takeMemories(@Nonnull Set<Memory> outMemories) {
|
||||
boolean result = outMemories.addAll(this.memories);
|
||||
this.memories.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public Set<Memory> getRecordedMemories() {
|
||||
return Collections.unmodifiableSet(this.memories);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.interactions;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.memories.MemoriesPlugin;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.map.Int2ObjectMapCodec;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.protocol.InteractionState;
|
||||
import com.hypixel.hytale.protocol.InteractionType;
|
||||
import com.hypixel.hytale.protocol.WaitForDataFrom;
|
||||
import com.hypixel.hytale.server.core.entity.InteractionContext;
|
||||
import com.hypixel.hytale.server.core.entity.InteractionManager;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.Interaction;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.data.Collector;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.data.CollectorTag;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.data.StringTag;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.operation.Label;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.operation.OperationsBuilder;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap.Entry;
|
||||
import java.util.Arrays;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class MemoriesConditionInteraction extends Interaction {
|
||||
@Nonnull
|
||||
public static final BuilderCodec<MemoriesConditionInteraction> CODEC = BuilderCodec.builder(
|
||||
MemoriesConditionInteraction.class, MemoriesConditionInteraction::new, ABSTRACT_CODEC
|
||||
)
|
||||
.appendInherited(
|
||||
new KeyedCodec<>("Next", new Int2ObjectMapCodec<>(Interaction.CHILD_ASSET_CODEC, Int2ObjectOpenHashMap::new)),
|
||||
(o, v) -> o.next = v,
|
||||
o -> o.next,
|
||||
(o, p) -> o.next = p.next
|
||||
)
|
||||
.documentation("The interaction to run if the player's memories level matches the key.")
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.<String>appendInherited(new KeyedCodec<>("Failed", Interaction.CHILD_ASSET_CODEC), (o, v) -> o.failed = v, o -> o.failed, (o, p) -> o.failed = p.failed)
|
||||
.documentation("The interaction to run if the player's memories level does not match any key.")
|
||||
.add()
|
||||
.afterDecode(o -> {
|
||||
o.levelToLabel.defaultReturnValue(-1);
|
||||
o.sortedKeys = o.next.keySet().toIntArray();
|
||||
Arrays.sort(o.sortedKeys);
|
||||
o.levelToLabel.clear();
|
||||
|
||||
for (int i = 0; i < o.sortedKeys.length; i++) {
|
||||
o.levelToLabel.put(o.sortedKeys[i], i);
|
||||
}
|
||||
})
|
||||
.build();
|
||||
@Nonnull
|
||||
private static final StringTag TAG_FAILED = StringTag.of("Failed");
|
||||
@Nonnull
|
||||
private Int2ObjectMap<String> next = Int2ObjectMaps.emptyMap();
|
||||
private transient int[] sortedKeys;
|
||||
@Nonnull
|
||||
private final Int2IntOpenHashMap levelToLabel = new Int2IntOpenHashMap();
|
||||
@Nullable
|
||||
private String failed;
|
||||
|
||||
public MemoriesConditionInteraction() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tick0(
|
||||
boolean firstRun, float time, @Nonnull InteractionType type, @Nonnull InteractionContext context, @Nonnull CooldownHandler cooldownHandler
|
||||
) {
|
||||
CommandBuffer<EntityStore> commandBuffer = context.getCommandBuffer();
|
||||
|
||||
assert commandBuffer != null;
|
||||
|
||||
World world = commandBuffer.getExternalData().getWorld();
|
||||
int memoriesLevel = MemoriesPlugin.get().getMemoriesLevel(world.getGameplayConfig());
|
||||
context.getState().chainingIndex = memoriesLevel;
|
||||
int labelIndex = this.levelToLabel.get(memoriesLevel);
|
||||
if (labelIndex == -1) {
|
||||
labelIndex = this.sortedKeys.length;
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
context.getState().state = InteractionState.Finished;
|
||||
}
|
||||
|
||||
context.jump(context.getLabel(labelIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void simulateTick0(
|
||||
boolean firstRun, float time, @Nonnull InteractionType type, @Nonnull InteractionContext context, @Nonnull CooldownHandler cooldownHandler
|
||||
) {
|
||||
int memoriesLevel = context.getServerState().chainingIndex;
|
||||
int labelIndex = this.levelToLabel.get(memoriesLevel);
|
||||
if (labelIndex == -1) {
|
||||
labelIndex = this.sortedKeys.length;
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
context.getState().state = InteractionState.Finished;
|
||||
}
|
||||
|
||||
context.jump(context.getLabel(labelIndex));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(@Nonnull OperationsBuilder builder) {
|
||||
Label end = builder.createUnresolvedLabel();
|
||||
Label[] labels = new Label[this.next.size() + 1];
|
||||
|
||||
for (int i = 0; i < labels.length; i++) {
|
||||
labels[i] = builder.createUnresolvedLabel();
|
||||
}
|
||||
|
||||
builder.addOperation(this, labels);
|
||||
builder.jump(end);
|
||||
|
||||
for (int i = 0; i < this.sortedKeys.length; i++) {
|
||||
int key = this.sortedKeys[i];
|
||||
builder.resolveLabel(labels[i]);
|
||||
Interaction interaction = Interaction.getInteractionOrUnknown(this.next.get(key));
|
||||
interaction.compile(builder);
|
||||
builder.jump(end);
|
||||
}
|
||||
|
||||
int failedIndex = this.sortedKeys.length;
|
||||
builder.resolveLabel(labels[failedIndex]);
|
||||
if (this.failed != null) {
|
||||
Interaction interaction = Interaction.getInteractionOrUnknown(this.failed);
|
||||
interaction.compile(builder);
|
||||
}
|
||||
|
||||
builder.resolveLabel(end);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
protected com.hypixel.hytale.protocol.Interaction generatePacket() {
|
||||
return new com.hypixel.hytale.protocol.MemoriesConditionInteraction();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configurePacket(com.hypixel.hytale.protocol.Interaction packet) {
|
||||
super.configurePacket(packet);
|
||||
com.hypixel.hytale.protocol.MemoriesConditionInteraction p = (com.hypixel.hytale.protocol.MemoriesConditionInteraction)packet;
|
||||
p.memoriesNext = new Int2IntOpenHashMap(this.next.size());
|
||||
|
||||
for (Entry<String> e : this.next.int2ObjectEntrySet()) {
|
||||
p.memoriesNext.put(e.getIntKey(), Interaction.getInteractionIdOrUnknown(e.getValue()));
|
||||
}
|
||||
|
||||
p.failed = Interaction.getInteractionIdOrUnknown(this.failed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean walk(@Nonnull Collector collector, @Nonnull InteractionContext context) {
|
||||
if (this.next != null) {
|
||||
for (Entry<String> entry : this.next.int2ObjectEntrySet()) {
|
||||
if (InteractionManager.walkInteraction(collector, context, new MemoriesConditionInteraction.MemoriesTag(entry.getIntKey()), entry.getValue())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.failed != null ? InteractionManager.walkInteraction(collector, context, TAG_FAILED, this.failed) : false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean needsRemoteSync() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WaitForDataFrom getWaitForDataFrom() {
|
||||
return WaitForDataFrom.Server;
|
||||
}
|
||||
|
||||
private record MemoriesTag(int memoryLevel) implements CollectorTag {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.interactions;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.memories.component.PlayerMemories;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.protocol.InteractionState;
|
||||
import com.hypixel.hytale.protocol.InteractionType;
|
||||
import com.hypixel.hytale.protocol.WaitForDataFrom;
|
||||
import com.hypixel.hytale.protocol.packets.player.UpdateMemoriesFeatureStatus;
|
||||
import com.hypixel.hytale.server.core.Message;
|
||||
import com.hypixel.hytale.server.core.entity.InteractionContext;
|
||||
import com.hypixel.hytale.server.core.io.PacketHandler;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.CooldownHandler;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.SimpleInstantInteraction;
|
||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import com.hypixel.hytale.server.core.util.NotificationUtil;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class SetMemoriesCapacityInteraction extends SimpleInstantInteraction {
|
||||
public static final BuilderCodec<SetMemoriesCapacityInteraction> CODEC = BuilderCodec.builder(
|
||||
SetMemoriesCapacityInteraction.class, SetMemoriesCapacityInteraction::new, SimpleInstantInteraction.CODEC
|
||||
)
|
||||
.documentation("Sets how many memories a player can store.")
|
||||
.<Integer>appendInherited(
|
||||
new KeyedCodec<>("Capacity", Codec.INTEGER), (i, s) -> i.capacity = s, i -> i.capacity, (i, parent) -> i.capacity = parent.capacity
|
||||
)
|
||||
.documentation("Defines the amount of memories that a player can store.")
|
||||
.add()
|
||||
.build();
|
||||
private int capacity;
|
||||
|
||||
public SetMemoriesCapacityInteraction() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void firstRun(@Nonnull InteractionType type, @Nonnull InteractionContext context, @Nonnull CooldownHandler cooldownHandler) {
|
||||
Ref<EntityStore> ref = context.getEntity();
|
||||
CommandBuffer<EntityStore> commandBuffer = context.getCommandBuffer();
|
||||
|
||||
assert commandBuffer != null;
|
||||
|
||||
PlayerMemories memoriesComponent = commandBuffer.ensureAndGetComponent(ref, PlayerMemories.getComponentType());
|
||||
if (this.capacity <= memoriesComponent.getMemoriesCapacity()) {
|
||||
context.getState().state = InteractionState.Failed;
|
||||
} else {
|
||||
int previousCapacity = memoriesComponent.getMemoriesCapacity();
|
||||
memoriesComponent.setMemoriesCapacity(this.capacity);
|
||||
if (previousCapacity <= 0) {
|
||||
PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType());
|
||||
|
||||
assert playerRefComponent != null;
|
||||
|
||||
PacketHandler playerConnection = playerRefComponent.getPacketHandler();
|
||||
playerConnection.writeNoCache(new UpdateMemoriesFeatureStatus(true));
|
||||
NotificationUtil.sendNotification(
|
||||
playerConnection, Message.translation("server.memories.general.featureUnlockedNotification"), null, "NotificationIcons/MemoriesIcon.png"
|
||||
);
|
||||
playerRefComponent.sendMessage(Message.translation("server.memories.general.featureUnlockedMessage"));
|
||||
}
|
||||
|
||||
context.getState().state = InteractionState.Finished;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public WaitForDataFrom getWaitForDataFrom() {
|
||||
return WaitForDataFrom.Server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.memories;
|
||||
|
||||
import com.hypixel.hytale.codec.lookup.CodecMapCodec;
|
||||
import com.hypixel.hytale.server.core.Message;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public abstract class Memory {
|
||||
public static final CodecMapCodec<Memory> CODEC = new CodecMapCodec<>();
|
||||
|
||||
public Memory() {
|
||||
}
|
||||
|
||||
public abstract String getId();
|
||||
|
||||
public abstract String getTitle();
|
||||
|
||||
public abstract Message getTooltipText();
|
||||
|
||||
@Nullable
|
||||
public abstract String getIconPath();
|
||||
|
||||
public abstract Message getUndiscoveredTooltipText();
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
return this == o || o != null && this.getClass() == o.getClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.getClass().hashCode();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Memory{}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.memories;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.memories.MemoriesPlugin;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class MemoryProvider<T extends Memory> {
|
||||
private final String id;
|
||||
private final BuilderCodec<T> codec;
|
||||
private final double defaultRadius;
|
||||
|
||||
public MemoryProvider(String id, BuilderCodec<T> codec, double defaultRadius) {
|
||||
this.id = id;
|
||||
this.codec = codec;
|
||||
this.defaultRadius = defaultRadius;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
public BuilderCodec<T> getCodec() {
|
||||
return this.codec;
|
||||
}
|
||||
|
||||
public double getCollectionRadius() {
|
||||
return MemoriesPlugin.get().getConfig().getCollectionRadius().getOrDefault(this.id, this.defaultRadius);
|
||||
}
|
||||
|
||||
public abstract Map<String, Set<Memory>> getAllMemories();
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.memories.npc;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.memories.MemoriesGameplayConfig;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.MemoriesPlugin;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.component.PlayerMemories;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.memories.Memory;
|
||||
import com.hypixel.hytale.builtin.instances.config.InstanceDiscoveryConfig;
|
||||
import com.hypixel.hytale.builtin.instances.config.InstanceWorldConfig;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.validation.Validators;
|
||||
import com.hypixel.hytale.component.AddReason;
|
||||
import com.hypixel.hytale.component.ArchetypeChunk;
|
||||
import com.hypixel.hytale.component.CommandBuffer;
|
||||
import com.hypixel.hytale.component.Holder;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.component.query.Query;
|
||||
import com.hypixel.hytale.component.spatial.SpatialResource;
|
||||
import com.hypixel.hytale.component.spatial.SpatialStructure;
|
||||
import com.hypixel.hytale.component.system.tick.EntityTickingSystem;
|
||||
import com.hypixel.hytale.math.util.MathUtil;
|
||||
import com.hypixel.hytale.math.vector.Vector3d;
|
||||
import com.hypixel.hytale.protocol.GameMode;
|
||||
import com.hypixel.hytale.protocol.packets.entities.SpawnModelParticles;
|
||||
import com.hypixel.hytale.server.core.Message;
|
||||
import com.hypixel.hytale.server.core.asset.type.model.config.ModelParticle;
|
||||
import com.hypixel.hytale.server.core.entity.entities.Player;
|
||||
import com.hypixel.hytale.server.core.inventory.ItemStack;
|
||||
import com.hypixel.hytale.server.core.modules.entity.EntityModule;
|
||||
import com.hypixel.hytale.server.core.modules.entity.component.BoundingBox;
|
||||
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
|
||||
import com.hypixel.hytale.server.core.modules.entity.item.ItemComponent;
|
||||
import com.hypixel.hytale.server.core.modules.entity.item.PickupItemComponent;
|
||||
import com.hypixel.hytale.server.core.modules.entity.tracker.NetworkId;
|
||||
import com.hypixel.hytale.server.core.modules.i18n.I18nModule;
|
||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import com.hypixel.hytale.server.core.universe.world.World;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import com.hypixel.hytale.server.core.util.NotificationUtil;
|
||||
import com.hypixel.hytale.server.npc.NPCPlugin;
|
||||
import com.hypixel.hytale.server.npc.entities.NPCEntity;
|
||||
import com.hypixel.hytale.server.npc.role.Role;
|
||||
import com.hypixel.hytale.server.worldgen.chunk.ChunkGenerator;
|
||||
import com.hypixel.hytale.server.worldgen.chunk.ZoneBiomeResult;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectList;
|
||||
import java.util.Objects;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class NPCMemory extends Memory {
|
||||
@Nonnull
|
||||
public static final String ID = "NPC";
|
||||
@Nonnull
|
||||
public static final BuilderCodec<NPCMemory> CODEC = BuilderCodec.builder(NPCMemory.class, NPCMemory::new)
|
||||
.append(new KeyedCodec<>("NPCRole", Codec.STRING), (npcMemory, s) -> npcMemory.npcRole = s, npcMemory -> npcMemory.npcRole)
|
||||
.addValidator(Validators.nonNull())
|
||||
.add()
|
||||
.append(new KeyedCodec<>("TranslationKey", Codec.STRING), (npcMemory, s) -> npcMemory.memoryTitleKey = s, npcMemory -> npcMemory.memoryTitleKey)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("IsMemoriesNameOverridden", Codec.BOOLEAN),
|
||||
(npcMemory, aBoolean) -> npcMemory.isMemoriesNameOverridden = aBoolean,
|
||||
npcMemory -> npcMemory.isMemoriesNameOverridden
|
||||
)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("CapturedTimestamp", Codec.LONG),
|
||||
(npcMemory, aDouble) -> npcMemory.capturedTimestamp = aDouble,
|
||||
npcMemory -> npcMemory.capturedTimestamp
|
||||
)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("FoundLocationZoneNameKey", Codec.STRING),
|
||||
(npcMemory, s) -> npcMemory.foundLocationZoneNameKey = s,
|
||||
npcMemory -> npcMemory.foundLocationZoneNameKey
|
||||
)
|
||||
.add()
|
||||
.append(
|
||||
new KeyedCodec<>("FoundLocationNameKey", Codec.STRING),
|
||||
(npcMemory, s) -> npcMemory.foundLocationGeneralNameKey = s,
|
||||
npcMemory -> npcMemory.foundLocationGeneralNameKey
|
||||
)
|
||||
.add()
|
||||
.afterDecode(NPCMemory::processConfig)
|
||||
.build();
|
||||
private String npcRole;
|
||||
private boolean isMemoriesNameOverridden;
|
||||
private long capturedTimestamp;
|
||||
private String foundLocationZoneNameKey;
|
||||
private String foundLocationGeneralNameKey;
|
||||
private String memoryTitleKey;
|
||||
|
||||
private NPCMemory() {
|
||||
}
|
||||
|
||||
public NPCMemory(@Nonnull String npcRole, @Nonnull String nameTranslationKey, boolean isMemoriesNameOverridden) {
|
||||
this.npcRole = npcRole;
|
||||
this.memoryTitleKey = nameTranslationKey;
|
||||
this.isMemoriesNameOverridden = isMemoriesNameOverridden;
|
||||
this.processConfig();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.npcRole;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getTitle() {
|
||||
return this.memoryTitleKey;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Message getTooltipText() {
|
||||
return Message.translation("server.memories.general.discovered.tooltipText");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getIconPath() {
|
||||
return "UI/Custom/Pages/Memories/npcs/" + this.npcRole + ".png";
|
||||
}
|
||||
|
||||
public void processConfig() {
|
||||
if (this.isMemoriesNameOverridden) {
|
||||
this.memoryTitleKey = "server.npcRoles." + this.npcRole + ".name";
|
||||
if (I18nModule.get().getMessage("en-US", this.memoryTitleKey) == null) {
|
||||
this.memoryTitleKey = "server.memories.names." + this.npcRole;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.memoryTitleKey == null || this.memoryTitleKey.isEmpty()) {
|
||||
this.memoryTitleKey = "server.npcRoles." + this.npcRole + ".name";
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Message getUndiscoveredTooltipText() {
|
||||
return Message.translation("server.memories.general.undiscovered.tooltipText");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public String getNpcRole() {
|
||||
return this.npcRole;
|
||||
}
|
||||
|
||||
public long getCapturedTimestamp() {
|
||||
return this.capturedTimestamp;
|
||||
}
|
||||
|
||||
public String getFoundLocationZoneNameKey() {
|
||||
return this.foundLocationZoneNameKey;
|
||||
}
|
||||
|
||||
public Message getLocationMessage() {
|
||||
if (this.foundLocationGeneralNameKey != null) {
|
||||
return Message.translation(this.foundLocationGeneralNameKey);
|
||||
} else {
|
||||
return this.foundLocationZoneNameKey != null ? Message.translation("server.map.region." + this.foundLocationZoneNameKey) : Message.raw("???");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || this.getClass() != o.getClass()) {
|
||||
return false;
|
||||
} else if (!super.equals(o)) {
|
||||
return false;
|
||||
} else {
|
||||
NPCMemory npcMemory = (NPCMemory)o;
|
||||
return this.isMemoriesNameOverridden == npcMemory.isMemoriesNameOverridden && Objects.equals(this.npcRole, npcMemory.npcRole);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + Objects.hashCode(this.npcRole);
|
||||
return 31 * result + Boolean.hashCode(this.isMemoriesNameOverridden);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NPCMemory{npcRole='"
|
||||
+ this.npcRole
|
||||
+ "', isMemoriesNameOverride="
|
||||
+ this.isMemoriesNameOverridden
|
||||
+ "', capturedTimestamp="
|
||||
+ this.capturedTimestamp
|
||||
+ "', foundLocationZoneNameKey='"
|
||||
+ this.foundLocationZoneNameKey
|
||||
+ "}";
|
||||
}
|
||||
|
||||
public static class GatherMemoriesSystem extends EntityTickingSystem<EntityStore> {
|
||||
@Nonnull
|
||||
public static final Query<EntityStore> QUERY = Query.and(
|
||||
TransformComponent.getComponentType(), Player.getComponentType(), PlayerMemories.getComponentType()
|
||||
);
|
||||
private final double radius;
|
||||
|
||||
public GatherMemoriesSystem(double radius) {
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(
|
||||
float dt,
|
||||
int index,
|
||||
@Nonnull ArchetypeChunk<EntityStore> archetypeChunk,
|
||||
@Nonnull Store<EntityStore> store,
|
||||
@Nonnull CommandBuffer<EntityStore> commandBuffer
|
||||
) {
|
||||
Player playerComponent = archetypeChunk.getComponent(index, Player.getComponentType());
|
||||
|
||||
assert playerComponent != null;
|
||||
|
||||
if (playerComponent.getGameMode() == GameMode.Adventure) {
|
||||
TransformComponent transformComponent = archetypeChunk.getComponent(index, TransformComponent.getComponentType());
|
||||
|
||||
assert transformComponent != null;
|
||||
|
||||
Vector3d position = transformComponent.getPosition();
|
||||
SpatialResource<Ref<EntityStore>, EntityStore> npcSpatialResource = store.getResource(NPCPlugin.get().getNpcSpatialResource());
|
||||
ObjectList<Ref<EntityStore>> results = SpatialResource.getThreadLocalReferenceList();
|
||||
npcSpatialResource.getSpatialStructure().collect(position, this.radius, results);
|
||||
if (!results.isEmpty()) {
|
||||
PlayerRef playerRefComponent = archetypeChunk.getComponent(index, PlayerRef.getComponentType());
|
||||
|
||||
assert playerRefComponent != null;
|
||||
|
||||
Ref<EntityStore> ref = archetypeChunk.getReferenceTo(index);
|
||||
MemoriesPlugin memoriesPlugin = MemoriesPlugin.get();
|
||||
PlayerMemories playerMemoriesComponent = archetypeChunk.getComponent(index, PlayerMemories.getComponentType());
|
||||
|
||||
assert playerMemoriesComponent != null;
|
||||
|
||||
NPCMemory temp = new NPCMemory();
|
||||
World world = commandBuffer.getExternalData().getWorld();
|
||||
String foundLocationZoneNameKey = findLocationZoneName(world, position);
|
||||
|
||||
for (Ref<EntityStore> npcRef : results) {
|
||||
NPCEntity npcComponent = commandBuffer.getComponent(npcRef, NPCEntity.getComponentType());
|
||||
if (npcComponent != null) {
|
||||
Role role = npcComponent.getRole();
|
||||
|
||||
assert role != null;
|
||||
|
||||
if (role.isMemory()) {
|
||||
temp.isMemoriesNameOverridden = role.isMemoriesNameOverriden();
|
||||
temp.npcRole = temp.isMemoriesNameOverridden ? role.getMemoriesNameOverride() : npcComponent.getRoleName();
|
||||
temp.memoryTitleKey = role.getNameTranslationKey();
|
||||
temp.capturedTimestamp = System.currentTimeMillis();
|
||||
temp.foundLocationGeneralNameKey = foundLocationZoneNameKey;
|
||||
if (!memoriesPlugin.hasRecordedMemory(temp)) {
|
||||
temp.processConfig();
|
||||
if (playerMemoriesComponent.recordMemory(temp)) {
|
||||
NotificationUtil.sendNotification(
|
||||
playerRefComponent.getPacketHandler(),
|
||||
Message.translation("server.memories.general.collected").param("memoryTitle", Message.translation(temp.getTitle())),
|
||||
null,
|
||||
"NotificationIcons/MemoriesIcon.png"
|
||||
);
|
||||
temp = new NPCMemory();
|
||||
TransformComponent npcTransformComponent = commandBuffer.getComponent(npcRef, TransformComponent.getComponentType());
|
||||
|
||||
assert npcTransformComponent != null;
|
||||
|
||||
MemoriesGameplayConfig memoriesGameplayConfig = MemoriesGameplayConfig.get(store.getExternalData().getWorld().getGameplayConfig());
|
||||
if (memoriesGameplayConfig != null) {
|
||||
ItemStack memoryItemStack = new ItemStack(memoriesGameplayConfig.getMemoriesCatchItemId());
|
||||
Vector3d memoryItemHolderPosition = npcTransformComponent.getPosition().clone();
|
||||
BoundingBox boundingBoxComponent = commandBuffer.getComponent(npcRef, BoundingBox.getComponentType());
|
||||
if (boundingBoxComponent != null) {
|
||||
memoryItemHolderPosition.y = memoryItemHolderPosition.y + boundingBoxComponent.getBoundingBox().middleY();
|
||||
}
|
||||
|
||||
Holder<EntityStore> memoryItemHolder = ItemComponent.generatePickedUpItem(
|
||||
memoryItemStack, memoryItemHolderPosition, commandBuffer, ref
|
||||
);
|
||||
float memoryCatchItemLifetimeS = 0.62F;
|
||||
PickupItemComponent pickupItemComponent = memoryItemHolder.getComponent(PickupItemComponent.getComponentType());
|
||||
|
||||
assert pickupItemComponent != null;
|
||||
|
||||
pickupItemComponent.setInitialLifeTime(0.62F);
|
||||
commandBuffer.addEntity(memoryItemHolder, AddReason.SPAWN);
|
||||
displayCatchEntityParticles(memoriesGameplayConfig, memoryItemHolderPosition, npcRef, commandBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static String findLocationZoneName(World world, Vector3d position) {
|
||||
if (world.getChunkStore().getGenerator() instanceof ChunkGenerator generator) {
|
||||
int seed = (int)world.getWorldConfig().getSeed();
|
||||
ZoneBiomeResult result = generator.getZoneBiomeResultAt(seed, MathUtil.floor(position.x), MathUtil.floor(position.z));
|
||||
return "server.map.region." + result.getZoneResult().getZone().name();
|
||||
} else {
|
||||
InstanceWorldConfig instanceConfig = world.getWorldConfig().getPluginConfig().get(InstanceWorldConfig.class);
|
||||
if (instanceConfig != null) {
|
||||
InstanceDiscoveryConfig discovery = instanceConfig.getDiscovery();
|
||||
if (discovery != null && discovery.getTitleKey() != null) {
|
||||
return discovery.getTitleKey();
|
||||
}
|
||||
}
|
||||
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
private static void displayCatchEntityParticles(
|
||||
MemoriesGameplayConfig memoriesGameplayConfig, Vector3d targetPosition, Ref<EntityStore> targetRef, @Nonnull CommandBuffer<EntityStore> commandBuffer
|
||||
) {
|
||||
ModelParticle particle = memoriesGameplayConfig.getMemoriesCatchEntityParticle();
|
||||
if (particle != null) {
|
||||
NetworkId networkIdComponent = commandBuffer.getComponent(targetRef, NetworkId.getComponentType());
|
||||
if (networkIdComponent != null) {
|
||||
com.hypixel.hytale.protocol.ModelParticle[] modelParticlesProtocol = new com.hypixel.hytale.protocol.ModelParticle[]{particle.toPacket()};
|
||||
SpawnModelParticles packet = new SpawnModelParticles(networkIdComponent.getId(), modelParticlesProtocol);
|
||||
SpatialResource<Ref<EntityStore>, EntityStore> spatialResource = commandBuffer.getResource(EntityModule.get().getPlayerSpatialResourceType());
|
||||
SpatialStructure<Ref<EntityStore>> spatialStructure = spatialResource.getSpatialStructure();
|
||||
ObjectList<Ref<EntityStore>> results = SpatialResource.getThreadLocalReferenceList();
|
||||
spatialStructure.ordered(targetPosition, memoriesGameplayConfig.getMemoriesCatchParticleViewDistance(), results);
|
||||
|
||||
for (Ref<EntityStore> ref : results) {
|
||||
PlayerRef playerRefComponent = commandBuffer.getComponent(ref, PlayerRef.getComponentType());
|
||||
|
||||
assert playerRefComponent != null;
|
||||
|
||||
playerRefComponent.getPacketHandler().write(packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Query<EntityStore> getQuery() {
|
||||
return QUERY;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.memories.npc;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.memories.MemoriesPlugin;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.memories.Memory;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.memories.MemoryProvider;
|
||||
import com.hypixel.hytale.logger.sentry.SkipSentryException;
|
||||
import com.hypixel.hytale.server.npc.NPCPlugin;
|
||||
import com.hypixel.hytale.server.npc.asset.builder.Builder;
|
||||
import com.hypixel.hytale.server.npc.asset.builder.BuilderInfo;
|
||||
import com.hypixel.hytale.server.npc.util.expression.ExecutionContext;
|
||||
import com.hypixel.hytale.server.npc.util.expression.Scope;
|
||||
import com.hypixel.hytale.server.spawning.ISpawnableWithModel;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class NPCMemoryProvider extends MemoryProvider<NPCMemory> {
|
||||
public static final double DEFAULT_RADIUS = 10.0;
|
||||
|
||||
public NPCMemoryProvider() {
|
||||
super("NPC", NPCMemory.CODEC, 10.0);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Map<String, Set<Memory>> getAllMemories() {
|
||||
Map<String, Set<Memory>> allMemories = new Object2ObjectOpenHashMap<>();
|
||||
Int2ObjectMap<BuilderInfo> allBuilders = NPCPlugin.get().getBuilderManager().getAllBuilders();
|
||||
|
||||
for (BuilderInfo builderInfo : allBuilders.values()) {
|
||||
try {
|
||||
Builder<?> builder = builderInfo.getBuilder();
|
||||
if (builder.isSpawnable() && !builder.isDeprecated() && builderInfo.isValid() && isMemory(builder)) {
|
||||
String category = getCategory(builder);
|
||||
if (category != null) {
|
||||
String memoriesNameOverride = getMemoriesNameOverride(builder);
|
||||
String translationKey = getNPCNameTranslationKey(builder);
|
||||
NPCMemory memory;
|
||||
if (memoriesNameOverride != null && !memoriesNameOverride.isEmpty()) {
|
||||
memory = new NPCMemory(memoriesNameOverride, translationKey, true);
|
||||
} else {
|
||||
memory = new NPCMemory(builderInfo.getKeyName(), translationKey, false);
|
||||
}
|
||||
|
||||
allMemories.computeIfAbsent(category, s -> new HashSet<>()).add(memory);
|
||||
}
|
||||
}
|
||||
} catch (SkipSentryException var10) {
|
||||
MemoriesPlugin.get().getLogger().at(Level.SEVERE).log(var10.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return allMemories;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getCategory(@Nonnull Builder<?> builder) {
|
||||
if (builder instanceof ISpawnableWithModel spawnableWithModel) {
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
executionContext.setScope(spawnableWithModel.createExecutionScope());
|
||||
Scope modifierScope = spawnableWithModel.createModifierScope(executionContext);
|
||||
return spawnableWithModel.getMemoriesCategory(executionContext, modifierScope);
|
||||
} else {
|
||||
return "Other";
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isMemory(@Nonnull Builder<?> builder) {
|
||||
if (builder instanceof ISpawnableWithModel spawnableWithModel) {
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
executionContext.setScope(spawnableWithModel.createExecutionScope());
|
||||
Scope modifierScope = spawnableWithModel.createModifierScope(executionContext);
|
||||
return spawnableWithModel.isMemory(executionContext, modifierScope);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static String getMemoriesNameOverride(@Nonnull Builder<?> builder) {
|
||||
if (builder instanceof ISpawnableWithModel spawnableWithModel) {
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
executionContext.setScope(spawnableWithModel.createExecutionScope());
|
||||
Scope modifierScope = spawnableWithModel.createModifierScope(executionContext);
|
||||
return spawnableWithModel.getMemoriesNameOverride(executionContext, modifierScope);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static String getNPCNameTranslationKey(@Nonnull Builder<?> builder) {
|
||||
if (builder instanceof ISpawnableWithModel spawnableWithModel) {
|
||||
ExecutionContext executionContext = new ExecutionContext();
|
||||
executionContext.setScope(spawnableWithModel.createExecutionScope());
|
||||
Scope modifierScope = spawnableWithModel.createModifierScope(executionContext);
|
||||
return spawnableWithModel.getNameTranslationKey(executionContext, modifierScope);
|
||||
} else {
|
||||
throw new SkipSentryException(new IllegalStateException("Cannot get translation key for a non spawnable NPC role!"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.page;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.memories.MemoriesGameplayConfig;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.MemoriesPlugin;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.component.PlayerMemories;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.memories.Memory;
|
||||
import com.hypixel.hytale.builtin.adventure.memories.memories.npc.NPCMemory;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.EnumCodec;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.component.spatial.SpatialResource;
|
||||
import com.hypixel.hytale.math.vector.Vector3d;
|
||||
import com.hypixel.hytale.protocol.BlockPosition;
|
||||
import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime;
|
||||
import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType;
|
||||
import com.hypixel.hytale.server.core.Message;
|
||||
import com.hypixel.hytale.server.core.asset.type.gameplay.GameplayConfig;
|
||||
import com.hypixel.hytale.server.core.entity.entities.Player;
|
||||
import com.hypixel.hytale.server.core.entity.entities.player.pages.InteractiveCustomUIPage;
|
||||
import com.hypixel.hytale.server.core.modules.entity.EntityModule;
|
||||
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
|
||||
import com.hypixel.hytale.server.core.ui.Anchor;
|
||||
import com.hypixel.hytale.server.core.ui.Value;
|
||||
import com.hypixel.hytale.server.core.ui.builder.EventData;
|
||||
import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder;
|
||||
import com.hypixel.hytale.server.core.ui.builder.UIEventBuilder;
|
||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import com.hypixel.hytale.server.core.universe.world.ParticleUtil;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectList;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneOffset;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.FormatStyle;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Map.Entry;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class MemoriesPage extends InteractiveCustomUIPage<MemoriesPage.PageEventData> {
|
||||
@Nullable
|
||||
private String currentCategory;
|
||||
@Nullable
|
||||
private Memory selectedMemory;
|
||||
private Vector3d recordMemoriesParticlesPosition;
|
||||
|
||||
public MemoriesPage(@Nonnull PlayerRef playerRef, BlockPosition blockPosition) {
|
||||
super(playerRef, CustomPageLifetime.CanDismissOrCloseThroughInteraction, MemoriesPage.PageEventData.CODEC);
|
||||
this.recordMemoriesParticlesPosition = new Vector3d(blockPosition.x, blockPosition.y, blockPosition.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(
|
||||
@Nonnull Ref<EntityStore> ref, @Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder, @Nonnull Store<EntityStore> store
|
||||
) {
|
||||
MemoriesPlugin memoriesPlugin = MemoriesPlugin.get();
|
||||
if (this.currentCategory == null) {
|
||||
commandBuilder.append("Pages/Memories/MemoriesCategoryPanel.ui");
|
||||
Map<String, Set<Memory>> allMemories = memoriesPlugin.getAllMemories();
|
||||
Set<Memory> recordedMemories = memoriesPlugin.getRecordedMemories();
|
||||
int totalMemories = 0;
|
||||
|
||||
for (Set<Memory> value : allMemories.values()) {
|
||||
totalMemories += value.size();
|
||||
}
|
||||
|
||||
commandBuilder.set("#MemoriesProgressBar.Value", (float)recordedMemories.size() / totalMemories);
|
||||
commandBuilder.set("#MemoriesProgressBarTexture.Value", (float)recordedMemories.size() / totalMemories);
|
||||
commandBuilder.set("#TotalCollected.Text", String.valueOf(recordedMemories.size()));
|
||||
commandBuilder.set("#MemoriesTotal.Text", String.valueOf(totalMemories));
|
||||
eventBuilder.addEventBinding(
|
||||
CustomUIEventBindingType.Activating, "#MemoriesInfoButton", new EventData().append("Action", MemoriesPage.PageAction.MemoriesInfo)
|
||||
);
|
||||
GameplayConfig gameplayConfig = store.getExternalData().getWorld().getGameplayConfig();
|
||||
PlayerMemories playerMemories = store.getComponent(ref, PlayerMemories.getComponentType());
|
||||
int i = 0;
|
||||
|
||||
for (Entry<String, Set<Memory>> entry : allMemories.entrySet()) {
|
||||
String category = entry.getKey();
|
||||
Set<Memory> memoriesInCategory = entry.getValue();
|
||||
String selector = "#IconList[" + i++ + "] ";
|
||||
int recordedMemoriesCount = 0;
|
||||
|
||||
for (Memory memory : memoriesInCategory) {
|
||||
if (recordedMemories.contains(memory)) {
|
||||
recordedMemoriesCount++;
|
||||
}
|
||||
}
|
||||
|
||||
commandBuilder.append("#IconList", "Pages/Memories/MemoriesCategory.ui");
|
||||
commandBuilder.set(selector + "#Button.Text", Message.translation("server.memories.categories." + category + ".title"));
|
||||
commandBuilder.set(selector + "#CurrentMemoryCountNotComplete.Text", String.valueOf(recordedMemoriesCount));
|
||||
commandBuilder.set(selector + "#CurrentMemoryCountComplete.Text", String.valueOf(recordedMemoriesCount));
|
||||
commandBuilder.set(selector + "#TotalMemoryCountNotComplete.Text", String.valueOf(memoriesInCategory.size()));
|
||||
commandBuilder.set(selector + "#TotalMemoryCountComplete.Text", String.valueOf(memoriesInCategory.size()));
|
||||
boolean isCategoryComplete = recordedMemoriesCount == memoriesInCategory.size();
|
||||
if (isCategoryComplete) {
|
||||
commandBuilder.set(selector + "#CategoryIcon.Background", "Pages/Memories/categories/" + category + "Complete.png");
|
||||
commandBuilder.set(selector + "#CompleteCategoryBackground.Visible", true);
|
||||
commandBuilder.set(selector + "#CompleteCategoryCounter.Visible", true);
|
||||
} else {
|
||||
commandBuilder.set(selector + "#CategoryIcon.Background", "Pages/Memories/categories/" + category + ".png");
|
||||
commandBuilder.set(selector + "#NotCompleteCategoryCounter.Visible", true);
|
||||
}
|
||||
|
||||
if (playerMemories != null) {
|
||||
Set<Memory> newMemories = playerMemories.getRecordedMemories();
|
||||
|
||||
for (Memory memoryx : memoriesInCategory) {
|
||||
if (newMemories.contains(memoryx)) {
|
||||
commandBuilder.set(selector + "#NewMemoryIndicator.Visible", true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
eventBuilder.addEventBinding(
|
||||
CustomUIEventBindingType.Activating,
|
||||
selector + "#Button",
|
||||
new EventData().append("Action", MemoriesPage.PageAction.ViewCategory).append("Category", category)
|
||||
);
|
||||
}
|
||||
|
||||
commandBuilder.set("#RecordButton.Visible", true);
|
||||
commandBuilder.set("#RecordButton.Disabled", playerMemories == null || !playerMemories.hasMemories());
|
||||
buildChestMarkers(commandBuilder, gameplayConfig, totalMemories);
|
||||
if (playerMemories != null && playerMemories.hasMemories()) {
|
||||
commandBuilder.set(
|
||||
"#RecordButton.Text", Message.translation("server.memories.general.recordNum").param("count", playerMemories.getRecordedMemories().size())
|
||||
);
|
||||
}
|
||||
|
||||
eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#RecordButton", new EventData().append("Action", MemoriesPage.PageAction.Record));
|
||||
} else {
|
||||
commandBuilder.append("Pages/Memories/MemoriesPanel.ui");
|
||||
Set<Memory> memoriesSet = memoriesPlugin.getAllMemories().get(this.currentCategory);
|
||||
ObjectArrayList<Memory> memories = new ObjectArrayList<>(memoriesSet);
|
||||
memories.sort(Comparator.comparing(Memory::getTitle));
|
||||
Set<Memory> recordedMemories = memoriesPlugin.getRecordedMemories();
|
||||
int recordedMemoriesCount = 0;
|
||||
|
||||
for (Memory memoryxx : memories) {
|
||||
if (recordedMemories.contains(memoryxx)) {
|
||||
recordedMemoriesCount++;
|
||||
}
|
||||
}
|
||||
|
||||
commandBuilder.set("#CategoryTitle.Text", Message.translation("server.memories.categories." + this.currentCategory + ".title"));
|
||||
commandBuilder.set("#CategoryCount.Text", recordedMemoriesCount + "/" + memories.size());
|
||||
|
||||
for (int i = 0; i < memories.size(); i++) {
|
||||
Memory memoryxxx = memories.get(i);
|
||||
String selector = "#IconList[" + i + "] ";
|
||||
commandBuilder.append("#IconList", "Pages/Memories/Memory.ui");
|
||||
boolean isDiscovered = recordedMemories.contains(memoryxxx);
|
||||
boolean isSelected = this.selectedMemory != null && this.selectedMemory.equals(memoryxxx);
|
||||
String buttonSelector = isSelected ? selector + "#ButtonSelected" : selector + "#ButtonNotSelected";
|
||||
if (isDiscovered) {
|
||||
commandBuilder.set(buttonSelector + ".Visible", true);
|
||||
commandBuilder.set(buttonSelector + ".TooltipText", memoryxxx.getTooltipText());
|
||||
commandBuilder.setNull(buttonSelector + ".Background");
|
||||
String iconPath = memoryxxx.getIconPath();
|
||||
if (iconPath != null && !iconPath.isEmpty()) {
|
||||
commandBuilder.set(selector + "#Icon.AssetPath", iconPath);
|
||||
}
|
||||
|
||||
eventBuilder.addEventBinding(
|
||||
CustomUIEventBindingType.Activating,
|
||||
buttonSelector,
|
||||
new EventData().append("Action", MemoriesPage.PageAction.SelectMemory).append("MemoryId", memoryxxx.getId())
|
||||
);
|
||||
} else {
|
||||
commandBuilder.set(selector + "#EmptyBackground.Visible", true);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.selectedMemory != null && recordedMemories.contains(this.selectedMemory)) {
|
||||
updateMemoryDetailsPanel(commandBuilder, this.selectedMemory);
|
||||
}
|
||||
|
||||
eventBuilder.addEventBinding(CustomUIEventBindingType.Activating, "#BackButton", new EventData().append("Action", MemoriesPage.PageAction.Back));
|
||||
}
|
||||
}
|
||||
|
||||
private static void buildChestMarkers(@Nonnull UICommandBuilder commandBuilder, @Nonnull GameplayConfig gameplayConfig, int totalMemories) {
|
||||
MemoriesGameplayConfig memoriesConfig = MemoriesGameplayConfig.get(gameplayConfig);
|
||||
if (memoriesConfig != null) {
|
||||
int[] memoriesAmountPerLevel = memoriesConfig.getMemoriesAmountPerLevel();
|
||||
if (memoriesAmountPerLevel != null && memoriesAmountPerLevel.length > 1) {
|
||||
MemoriesPlugin memoriesPlugin = MemoriesPlugin.get();
|
||||
int recordedMemoriesCount = memoriesPlugin.getRecordedMemories().size();
|
||||
int PROGRESS_BAR_PADDING = 18;
|
||||
int PROGRESS_BAR_WIDTH = 1018;
|
||||
int CHEST_POSITION_AREA = 1000;
|
||||
|
||||
for (int i = 0; i < memoriesAmountPerLevel.length; i++) {
|
||||
int memoryAmount = memoriesAmountPerLevel[i];
|
||||
boolean hasReachedLevel = recordedMemoriesCount >= memoryAmount;
|
||||
String selector = "#ChestMarkers[" + i + "]";
|
||||
Anchor anchor = new Anchor();
|
||||
int left = memoryAmount * 1000 / totalMemories;
|
||||
commandBuilder.append("#ChestMarkers", "Pages/Memories/ChestMarker.ui");
|
||||
anchor.setLeft(Value.of(left));
|
||||
commandBuilder.setObject(selector + ".Anchor", anchor);
|
||||
Message rewardsMessage = Message.translation("server.memories.general.chestActive.level" + (i + 1) + ".rewards");
|
||||
if (hasReachedLevel) {
|
||||
Message memoriesUnlockedMessage = Message.translation("server.memories.general.chestActive.tooltipText").param("count", memoryAmount);
|
||||
Message activeTooltipMessage = memoriesUnlockedMessage.insert("\n").insert(rewardsMessage);
|
||||
commandBuilder.set(selector + " #Arrow.Visible", true);
|
||||
commandBuilder.set(selector + " #ChestActive.Visible", true);
|
||||
commandBuilder.set(selector + " #ChestActive.TooltipTextSpans", activeTooltipMessage);
|
||||
} else {
|
||||
commandBuilder.set(selector + " #ChestDisabled.Visible", true);
|
||||
Message memoriesToUnlockMessage = Message.translation("server.memories.general.chestLocked.tooltipText").param("count", memoryAmount);
|
||||
Message disabledTooltipMessage = memoriesToUnlockMessage.insert("\n").insert(rewardsMessage);
|
||||
commandBuilder.set(selector + " #ChestDisabled.TooltipTextSpans", disabledTooltipMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleDataEvent(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull MemoriesPage.PageEventData data) {
|
||||
Player player = store.getComponent(ref, Player.getComponentType());
|
||||
|
||||
assert player != null;
|
||||
|
||||
TransformComponent transformComponent = store.getComponent(ref, TransformComponent.getComponentType());
|
||||
|
||||
assert transformComponent != null;
|
||||
|
||||
switch (data.action) {
|
||||
case Record:
|
||||
PlayerMemories playerMemories = store.getComponent(ref, PlayerMemories.getComponentType());
|
||||
if (playerMemories == null) {
|
||||
this.sendUpdate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MemoriesPlugin.get().recordPlayerMemories(playerMemories)) {
|
||||
this.rebuild();
|
||||
return;
|
||||
}
|
||||
|
||||
MemoriesGameplayConfig memoriesGameplayConfig = MemoriesGameplayConfig.get(store.getExternalData().getWorld().getGameplayConfig());
|
||||
if (memoriesGameplayConfig != null) {
|
||||
SpatialResource<Ref<EntityStore>, EntityStore> playerSpatialResource = store.getResource(EntityModule.get().getPlayerSpatialResourceType());
|
||||
ObjectList<Ref<EntityStore>> results = SpatialResource.getThreadLocalReferenceList();
|
||||
playerSpatialResource.getSpatialStructure().collect(this.recordMemoriesParticlesPosition, 75.0, results);
|
||||
ParticleUtil.spawnParticleEffect(memoriesGameplayConfig.getMemoriesRecordParticles(), this.recordMemoriesParticlesPosition, results, store);
|
||||
}
|
||||
|
||||
this.close();
|
||||
break;
|
||||
case ViewCategory:
|
||||
this.currentCategory = data.category;
|
||||
this.selectedMemory = null;
|
||||
this.rebuild();
|
||||
break;
|
||||
case Back:
|
||||
this.currentCategory = null;
|
||||
this.selectedMemory = null;
|
||||
this.rebuild();
|
||||
break;
|
||||
case MemoriesInfo:
|
||||
BlockPosition blockPostion = new BlockPosition(
|
||||
(int)this.recordMemoriesParticlesPosition.x, (int)this.recordMemoriesParticlesPosition.y, (int)this.recordMemoriesParticlesPosition.z
|
||||
);
|
||||
player.getPageManager().openCustomPage(ref, store, new MemoriesUnlockedPage(this.playerRef, blockPostion));
|
||||
break;
|
||||
case SelectMemory:
|
||||
if (data.memoryId == null || this.currentCategory == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Set<Memory> memoriesSet = MemoriesPlugin.get().getAllMemories().get(this.currentCategory);
|
||||
if (memoriesSet == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ObjectArrayList<Memory> memories = new ObjectArrayList<>(memoriesSet);
|
||||
memories.sort(Comparator.comparing(Memory::getTitle));
|
||||
Set<Memory> recordedMemories = MemoriesPlugin.get().getRecordedMemories();
|
||||
if (recordedMemories == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Memory newSelection = null;
|
||||
Iterator commandBuilder = recordedMemories.iterator();
|
||||
|
||||
while (true) {
|
||||
if (commandBuilder.hasNext()) {
|
||||
Memory memory = (Memory)commandBuilder.next();
|
||||
if (!memory.getId().equals(data.memoryId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newSelection = memory;
|
||||
}
|
||||
|
||||
if (newSelection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!memories.contains(newSelection) || newSelection.equals(this.selectedMemory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
UICommandBuilder commandBuilderx = new UICommandBuilder();
|
||||
if (this.selectedMemory != null && recordedMemories.contains(this.selectedMemory)) {
|
||||
int previousIndex = memories.indexOf(this.selectedMemory);
|
||||
if (previousIndex >= 0) {
|
||||
updateMemoryButtonSelection(commandBuilderx, previousIndex, this.selectedMemory, false);
|
||||
}
|
||||
}
|
||||
|
||||
int newIndex = memories.indexOf(newSelection);
|
||||
if (newIndex >= 0) {
|
||||
updateMemoryButtonSelection(commandBuilderx, newIndex, newSelection, true);
|
||||
}
|
||||
|
||||
updateMemoryDetailsPanel(commandBuilderx, newSelection);
|
||||
this.selectedMemory = newSelection;
|
||||
this.sendUpdate(commandBuilderx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateMemoryButtonSelection(@Nonnull UICommandBuilder commandBuilder, int index, @Nonnull Memory memory, boolean isSelected) {
|
||||
String selector = "#IconList[" + index + "] ";
|
||||
if (isSelected) {
|
||||
commandBuilder.set(selector + "#ButtonNotSelected.Visible", false);
|
||||
commandBuilder.set(selector + "#ButtonSelected.Visible", true);
|
||||
commandBuilder.setNull(selector + "#ButtonSelected.Background");
|
||||
commandBuilder.set(selector + "#ButtonSelected.TooltipText", memory.getTooltipText());
|
||||
} else {
|
||||
commandBuilder.set(selector + "#ButtonSelected.Visible", false);
|
||||
commandBuilder.set(selector + "#ButtonNotSelected.Visible", true);
|
||||
commandBuilder.setNull(selector + "#ButtonNotSelected.Background");
|
||||
commandBuilder.set(selector + "#ButtonNotSelected.TooltipText", memory.getTooltipText());
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateMemoryDetailsPanel(@Nonnull UICommandBuilder commandBuilder, @Nonnull Memory memory) {
|
||||
commandBuilder.set("#MemoryName.Text", Message.translation(memory.getTitle()));
|
||||
commandBuilder.set("#MemoryTimeLocation.Text", "");
|
||||
if (memory instanceof NPCMemory npcMemory) {
|
||||
Message locationNameKey = npcMemory.getLocationMessage();
|
||||
long capturedTimestamp = npcMemory.getCapturedTimestamp();
|
||||
String timeString = DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT)
|
||||
.withLocale(Locale.getDefault())
|
||||
.format(Instant.ofEpochMilli(capturedTimestamp).atZone(ZoneOffset.UTC));
|
||||
Message memoryLocationTimeText = Message.translation("server.memories.general.foundIn").param("location", locationNameKey).param("time", timeString);
|
||||
commandBuilder.set("#MemoryTimeLocation.TextSpans", memoryLocationTimeText);
|
||||
}
|
||||
|
||||
String iconPath = memory.getIconPath();
|
||||
if (iconPath != null && !iconPath.isEmpty()) {
|
||||
commandBuilder.set("#MemoryIcon.AssetPath", iconPath);
|
||||
} else {
|
||||
commandBuilder.setNull("#MemoryIcon.AssetPath");
|
||||
}
|
||||
}
|
||||
|
||||
public static enum PageAction {
|
||||
Record,
|
||||
ViewCategory,
|
||||
Back,
|
||||
MemoriesInfo,
|
||||
SelectMemory;
|
||||
|
||||
public static final Codec<MemoriesPage.PageAction> CODEC = new EnumCodec<>(MemoriesPage.PageAction.class);
|
||||
|
||||
private PageAction() {
|
||||
}
|
||||
}
|
||||
|
||||
public static class PageEventData {
|
||||
public static final String KEY_ACTION = "Action";
|
||||
public static final String KEY_CATEGORY = "Category";
|
||||
public static final String KEY_MEMORY_ID = "MemoryId";
|
||||
public static final BuilderCodec<MemoriesPage.PageEventData> CODEC = BuilderCodec.builder(
|
||||
MemoriesPage.PageEventData.class, MemoriesPage.PageEventData::new
|
||||
)
|
||||
.append(
|
||||
new KeyedCodec<>("Action", MemoriesPage.PageAction.CODEC),
|
||||
(pageEventData, pageAction) -> pageEventData.action = pageAction,
|
||||
pageEventData -> pageEventData.action
|
||||
)
|
||||
.add()
|
||||
.append(new KeyedCodec<>("Category", Codec.STRING), (pageEventData, s) -> pageEventData.category = s, pageEventData -> pageEventData.category)
|
||||
.add()
|
||||
.append(new KeyedCodec<>("MemoryId", Codec.STRING), (pageEventData, id) -> pageEventData.memoryId = id, pageEventData -> pageEventData.memoryId)
|
||||
.add()
|
||||
.build();
|
||||
public MemoriesPage.PageAction action;
|
||||
public String category;
|
||||
public String memoryId;
|
||||
|
||||
public PageEventData() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.page;
|
||||
|
||||
import com.hypixel.hytale.component.ComponentAccessor;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.server.core.entity.InteractionContext;
|
||||
import com.hypixel.hytale.server.core.entity.entities.player.pages.CustomUIPage;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.OpenCustomUIInteraction;
|
||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class MemoriesPageSupplier implements OpenCustomUIInteraction.CustomPageSupplier {
|
||||
public MemoriesPageSupplier() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CustomUIPage tryCreate(Ref<EntityStore> ref, ComponentAccessor<EntityStore> componentAccessor, PlayerRef playerRef, InteractionContext context) {
|
||||
return new MemoriesPage(playerRef, context.getTargetBlock());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.page;
|
||||
|
||||
import com.hypixel.hytale.builtin.adventure.memories.MemoriesPlugin;
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.codec.codecs.EnumCodec;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.component.Store;
|
||||
import com.hypixel.hytale.protocol.BlockPosition;
|
||||
import com.hypixel.hytale.protocol.packets.interface_.CustomPageLifetime;
|
||||
import com.hypixel.hytale.protocol.packets.interface_.CustomUIEventBindingType;
|
||||
import com.hypixel.hytale.server.core.entity.entities.Player;
|
||||
import com.hypixel.hytale.server.core.entity.entities.player.pages.InteractiveCustomUIPage;
|
||||
import com.hypixel.hytale.server.core.ui.builder.EventData;
|
||||
import com.hypixel.hytale.server.core.ui.builder.UICommandBuilder;
|
||||
import com.hypixel.hytale.server.core.ui.builder.UIEventBuilder;
|
||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class MemoriesUnlockedPage extends InteractiveCustomUIPage<MemoriesUnlockedPage.PageEventData> {
|
||||
private final BlockPosition blockPosition;
|
||||
|
||||
public MemoriesUnlockedPage(@Nonnull PlayerRef playerRef, BlockPosition blockPosition) {
|
||||
super(playerRef, CustomPageLifetime.CanDismissOrCloseThroughInteraction, MemoriesUnlockedPage.PageEventData.CODEC);
|
||||
this.blockPosition = blockPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(
|
||||
@Nonnull Ref<EntityStore> ref, @Nonnull UICommandBuilder commandBuilder, @Nonnull UIEventBuilder eventBuilder, @Nonnull Store<EntityStore> store
|
||||
) {
|
||||
MemoriesPlugin memoriesPlugin = MemoriesPlugin.get();
|
||||
commandBuilder.append("Pages/Memories/MemoriesUnlocked.ui");
|
||||
eventBuilder.addEventBinding(
|
||||
CustomUIEventBindingType.Activating, "#DiscoverMemoriesButton", new EventData().append("Action", MemoriesUnlockedPage.PageAction.DiscoverMemories)
|
||||
);
|
||||
}
|
||||
|
||||
public void handleDataEvent(@Nonnull Ref<EntityStore> ref, @Nonnull Store<EntityStore> store, @Nonnull MemoriesUnlockedPage.PageEventData data) {
|
||||
Player player = store.getComponent(ref, Player.getComponentType());
|
||||
|
||||
assert player != null;
|
||||
|
||||
if (data.action == MemoriesUnlockedPage.PageAction.DiscoverMemories) {
|
||||
player.getPageManager().openCustomPage(ref, store, new MemoriesPage(this.playerRef, this.blockPosition));
|
||||
}
|
||||
}
|
||||
|
||||
public static enum PageAction {
|
||||
DiscoverMemories;
|
||||
|
||||
public static final Codec<MemoriesUnlockedPage.PageAction> CODEC = new EnumCodec<>(MemoriesUnlockedPage.PageAction.class);
|
||||
|
||||
private PageAction() {
|
||||
}
|
||||
}
|
||||
|
||||
public static class PageEventData {
|
||||
public static final String KEY_ACTION = "Action";
|
||||
public static final BuilderCodec<MemoriesUnlockedPage.PageEventData> CODEC = BuilderCodec.builder(
|
||||
MemoriesUnlockedPage.PageEventData.class, MemoriesUnlockedPage.PageEventData::new
|
||||
)
|
||||
.append(
|
||||
new KeyedCodec<>("Action", MemoriesUnlockedPage.PageAction.CODEC),
|
||||
(pageEventData, pageAction) -> pageEventData.action = pageAction,
|
||||
pageEventData -> pageEventData.action
|
||||
)
|
||||
.add()
|
||||
.build();
|
||||
public MemoriesUnlockedPage.PageAction action;
|
||||
|
||||
public PageEventData() {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.page;
|
||||
|
||||
import com.hypixel.hytale.component.ComponentAccessor;
|
||||
import com.hypixel.hytale.component.Ref;
|
||||
import com.hypixel.hytale.server.core.entity.InteractionContext;
|
||||
import com.hypixel.hytale.server.core.entity.entities.player.pages.CustomUIPage;
|
||||
import com.hypixel.hytale.server.core.modules.interaction.interaction.config.server.OpenCustomUIInteraction;
|
||||
import com.hypixel.hytale.server.core.universe.PlayerRef;
|
||||
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class MemoriesUnlockedPageSuplier implements OpenCustomUIInteraction.CustomPageSupplier {
|
||||
public MemoriesUnlockedPageSuplier() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public CustomUIPage tryCreate(Ref<EntityStore> ref, ComponentAccessor<EntityStore> componentAccessor, PlayerRef playerRef, InteractionContext context) {
|
||||
return new MemoriesUnlockedPage(playerRef, context.getTargetBlock());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.hypixel.hytale.builtin.adventure.memories.temple;
|
||||
|
||||
import com.hypixel.hytale.codec.Codec;
|
||||
import com.hypixel.hytale.codec.KeyedCodec;
|
||||
import com.hypixel.hytale.codec.builder.BuilderCodec;
|
||||
import com.hypixel.hytale.server.core.asset.type.soundevent.config.SoundEvent;
|
||||
|
||||
public class ForgottenTempleConfig {
|
||||
public static final BuilderCodec<ForgottenTempleConfig> CODEC = BuilderCodec.builder(ForgottenTempleConfig.class, ForgottenTempleConfig::new)
|
||||
.append(new KeyedCodec<>("MinYRespawn", Codec.DOUBLE), (config, o) -> config.minYRespawn = o, config -> config.minYRespawn)
|
||||
.documentation("The Y at which players are teleported back to spawn.")
|
||||
.add()
|
||||
.<String>append(new KeyedCodec<>("RespawnSound", Codec.STRING), (config, o) -> config.respawnSound = o, config -> config.respawnSound)
|
||||
.documentation("The sound ID to play when players respawn in the temple.")
|
||||
.add()
|
||||
.build();
|
||||
private double minYRespawn = 5.0;
|
||||
private String respawnSound;
|
||||
|
||||
public ForgottenTempleConfig() {
|
||||
}
|
||||
|
||||
public double getMinYRespawn() {
|
||||
return this.minYRespawn;
|
||||
}
|
||||
|
||||
public String getRespawnSound() {
|
||||
return this.respawnSound;
|
||||
}
|
||||
|
||||
public int getRespawnSoundIndex() {
|
||||
return this.respawnSound == null ? 0 : SoundEvent.getAssetMap().getIndex(this.respawnSound);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user