mirror of
https://github.com/Minestom/Minestom.git
synced 2024-11-16 07:35:35 +01:00
Merge pull request #384 from Minestom/remove-resource-gatherer
Remove resource gatherer
This commit is contained in:
commit
3d2f61fd23
@ -164,7 +164,7 @@ dependencies {
|
||||
}
|
||||
|
||||
api "com.github.Minestom:DependencyGetter:v1.0.1"
|
||||
implementation 'com.github.Minestom:MinestomDataGenerator:b13c934eba'
|
||||
implementation 'com.github.Minestom:MinestomDataGenerator:7636d5d473'
|
||||
|
||||
// Adventure, for user-interface
|
||||
api "net.kyori:adventure-api:$adventureVersion"
|
||||
|
@ -25,7 +25,7 @@ dependencies {
|
||||
// SLF4J is the base logger for most libraries, therefore we can hook it into log4j2.
|
||||
implementation 'org.apache.logging.log4j:log4j-slf4j-impl:2.14.1'
|
||||
// Contains the json files
|
||||
implementation 'com.github.Minestom:MinestomDataGenerator:b13c934eba'
|
||||
implementation 'com.github.Minestom:MinestomDataGenerator:7636d5d473'
|
||||
}
|
||||
|
||||
|
||||
|
@ -12,7 +12,6 @@ import net.minestom.server.exception.ExceptionManager;
|
||||
import net.minestom.server.extensions.Extension;
|
||||
import net.minestom.server.extensions.ExtensionManager;
|
||||
import net.minestom.server.fluid.Fluid;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
import net.minestom.server.gamedata.tags.TagManager;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.InstanceManager;
|
||||
@ -28,7 +27,6 @@ import net.minestom.server.network.packet.server.play.ServerDifficultyPacket;
|
||||
import net.minestom.server.network.packet.server.play.UpdateViewDistancePacket;
|
||||
import net.minestom.server.ping.ResponseDataConsumer;
|
||||
import net.minestom.server.recipe.RecipeManager;
|
||||
import net.minestom.server.registry.ResourceGatherer;
|
||||
import net.minestom.server.scoreboard.TeamManager;
|
||||
import net.minestom.server.storage.StorageLocation;
|
||||
import net.minestom.server.storage.StorageManager;
|
||||
@ -46,8 +44,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* The main server class used to start the server and retrieve all the managers.
|
||||
* <p>
|
||||
@ -132,7 +128,6 @@ public final class MinecraftServer {
|
||||
private static ResponseDataConsumer responseDataConsumer;
|
||||
private static String brandName = "Minestom";
|
||||
private static Difficulty difficulty = Difficulty.NORMAL;
|
||||
private static LootTableManager lootTableManager;
|
||||
private static TagManager tagManager;
|
||||
|
||||
public static MinecraftServer init() {
|
||||
@ -171,18 +166,10 @@ public final class MinecraftServer {
|
||||
|
||||
updateManager = new UpdateManager();
|
||||
|
||||
lootTableManager = new LootTableManager();
|
||||
tagManager = new TagManager();
|
||||
|
||||
nettyServer = new NettyServer(packetProcessor);
|
||||
|
||||
// Registry
|
||||
try {
|
||||
ResourceGatherer.ensureResourcesArePresent(VERSION_NAME);
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("An error happened during resource gathering. Minestom will attempt to load anyway, but things may not work, and crashes can happen.", e);
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
||||
minecraftServer = new MinecraftServer();
|
||||
@ -619,16 +606,6 @@ public final class MinecraftServer {
|
||||
return responseDataConsumer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling loot tables.
|
||||
*
|
||||
* @return the loot table manager
|
||||
*/
|
||||
public static LootTableManager getLootTableManager() {
|
||||
checkInitStatus(lootTableManager);
|
||||
return lootTableManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the manager handling dimensions.
|
||||
*
|
||||
|
@ -1,20 +0,0 @@
|
||||
package net.minestom.server.gamedata;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
|
||||
/**
|
||||
* Represents a condition, used by predicates in MC functions and in loot tables.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Condition {
|
||||
|
||||
/**
|
||||
* Tests this condition. Subclasses are free to throw runtime exceptions if the arguments passed through data are not valid or missing
|
||||
* @param data arguments to give to the condition. May be null if the condition supports it
|
||||
* @return 'true' if the condition passed, 'false' otherwise
|
||||
*/
|
||||
boolean test(Data data);
|
||||
|
||||
Condition ALWAYS_YES = (_d) -> true;
|
||||
Condition ALWAYS_NO = (_d) -> false;
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package net.minestom.server.gamedata.conditions;
|
||||
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Requires 'explosionPower' double argument
|
||||
*/
|
||||
public class SurvivesExplosionCondition implements Condition {
|
||||
private Random rng = new Random();
|
||||
|
||||
@Override
|
||||
public boolean test(Data data) {
|
||||
if(data == null)
|
||||
return true; // no explosion here
|
||||
if(!data.hasKey("explosionPower"))
|
||||
return true; // no explosion here
|
||||
return rng.nextDouble() <= 1.0/data.<Double>get("explosionPower");
|
||||
}
|
||||
|
||||
public static class Deserializer implements JsonDeserializer<SurvivesExplosionCondition> {
|
||||
@Override
|
||||
public SurvivesExplosionCondition deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
return new SurvivesExplosionCondition();
|
||||
}
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables;
|
||||
|
||||
import com.google.gson.*;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public abstract class ConditionContainer {
|
||||
private ConditionContainer() {}
|
||||
|
||||
public abstract Condition create(LootTableManager lootTableManager);
|
||||
|
||||
|
||||
static class Deserializer implements JsonDeserializer<ConditionContainer> {
|
||||
|
||||
private final LootTableManager lootTableManager;
|
||||
|
||||
Deserializer(LootTableManager lootTableManager) {
|
||||
this.lootTableManager = lootTableManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConditionContainer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
JsonObject obj = json.getAsJsonObject();
|
||||
String type = obj.get("condition").getAsString();
|
||||
JsonDeserializer<? extends Condition> deserializer = lootTableManager.getConditionDeserializer(NamespaceID.from(type));
|
||||
return new ConditionContainer() {
|
||||
@Override
|
||||
public Condition create(LootTableManager lootTableManager) {
|
||||
return deserializer.deserialize(obj, typeOfT, context);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Loot table function that must meet some conditions to be applied
|
||||
*/
|
||||
public class ConditionedFunctionWrapper implements LootTableFunction {
|
||||
|
||||
private final LootTableFunction baseFunction;
|
||||
private final Collection<Condition> conditions;
|
||||
|
||||
public ConditionedFunctionWrapper(LootTableFunction baseFunction, Collection<Condition> conditions) {
|
||||
this.baseFunction = baseFunction;
|
||||
this.conditions = conditions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack apply(ItemStack stack, Data data) {
|
||||
for (Condition c : conditions) {
|
||||
if (!c.test(data))
|
||||
return stack;
|
||||
}
|
||||
return baseFunction.apply(stack, data);
|
||||
}
|
||||
}
|
@ -1,134 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.utils.WeightedRandom;
|
||||
import net.minestom.server.utils.WeightedRandomItem;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class LootTable {
|
||||
|
||||
public static final String LUCK_KEY = "minecraft:luck";
|
||||
|
||||
private final LootTableType type;
|
||||
private final List<LootTable.Pool> pools;
|
||||
|
||||
public LootTable(LootTableType type, List<Pool> pools) {
|
||||
this.type = type;
|
||||
this.pools = pools;
|
||||
}
|
||||
|
||||
public LootTableType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public List<Pool> getPools() {
|
||||
return pools;
|
||||
}
|
||||
|
||||
public List<ItemStack> generate(Data arguments) {
|
||||
if (arguments == null)
|
||||
arguments = Data.EMPTY;
|
||||
List<ItemStack> output = new LinkedList<>();
|
||||
for (Pool p : pools) {
|
||||
p.generate(output, arguments);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public static class Pool {
|
||||
private final int minRollCount;
|
||||
private final int maxRollCount;
|
||||
private final int bonusMinRollCount;
|
||||
private final int bonusMaxRollCount;
|
||||
private final List<LootTable.Entry> entries;
|
||||
private final List<Condition> conditions;
|
||||
|
||||
public Pool(int minRollCount, int maxRollCount, int bonusMinRollCount, int bonusMaxRollCount, List<Entry> entries, List<Condition> conditions) {
|
||||
this.minRollCount = minRollCount;
|
||||
this.maxRollCount = maxRollCount;
|
||||
this.bonusMinRollCount = bonusMinRollCount;
|
||||
this.bonusMaxRollCount = bonusMaxRollCount;
|
||||
this.entries = entries;
|
||||
this.conditions = conditions;
|
||||
}
|
||||
|
||||
public List<Condition> getConditions() {
|
||||
return conditions;
|
||||
}
|
||||
|
||||
public int getMinRollCount() {
|
||||
return minRollCount;
|
||||
}
|
||||
|
||||
public int getMaxRollCount() {
|
||||
return maxRollCount;
|
||||
}
|
||||
|
||||
public List<Entry> getEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
public void generate(List<ItemStack> output, Data arguments) {
|
||||
for (Condition c : conditions) {
|
||||
if (!c.test(arguments))
|
||||
return;
|
||||
}
|
||||
Random rng = new Random();
|
||||
int luck = arguments.getOrDefault(LUCK_KEY, 0);
|
||||
int rollCount = rng.nextInt(maxRollCount - minRollCount + 1 /*inclusive*/) + minRollCount;
|
||||
int bonusRollCount = rng.nextInt(bonusMaxRollCount - bonusMinRollCount + 1 /*inclusive*/) + bonusMinRollCount;
|
||||
bonusRollCount *= luck;
|
||||
// TODO: implement luck (quality/weight) weight=floor( weight + (quality * generic.luck))
|
||||
WeightedRandom<Entry> weightedRandom = new WeightedRandom<>(entries);
|
||||
for (int i = 0; i < rollCount + bonusRollCount; i++) {
|
||||
Entry entry = weightedRandom.get(rng);
|
||||
entry.generateStacks(output, arguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class Entry implements WeightedRandomItem {
|
||||
private final LootTableEntryType type;
|
||||
private final int weight;
|
||||
private final int quality;
|
||||
private final List<Condition> conditions;
|
||||
|
||||
public Entry(LootTableEntryType type, int weight, int quality, List<Condition> conditions) {
|
||||
this.type = type;
|
||||
this.weight = weight;
|
||||
this.quality = quality;
|
||||
this.conditions = conditions;
|
||||
}
|
||||
|
||||
public List<Condition> getConditions() {
|
||||
return conditions;
|
||||
}
|
||||
|
||||
public int getQuality() {
|
||||
return quality;
|
||||
}
|
||||
|
||||
public double getWeight() {
|
||||
return weight;
|
||||
}
|
||||
|
||||
public LootTableEntryType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public final void generateStacks(List<ItemStack> output, Data arguments) {
|
||||
for (Condition c : conditions) {
|
||||
if (!c.test(arguments))
|
||||
return;
|
||||
}
|
||||
generate(output, arguments);
|
||||
}
|
||||
|
||||
protected abstract void generate(List<ItemStack> output, Data arguments);
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables;
|
||||
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Meant only for parsing loot tables
|
||||
*/
|
||||
class LootTableContainer {
|
||||
|
||||
|
||||
private String type;
|
||||
private LootTableContainer.Pool[] pools;
|
||||
|
||||
private LootTableContainer() {}
|
||||
|
||||
public LootTable createTable(LootTableManager lootTableManager) {
|
||||
LootTableType type = lootTableManager.getTableType(NamespaceID.from(this.type));
|
||||
List<LootTable.Pool> pools = new LinkedList<>();
|
||||
if(this.pools != null) {
|
||||
for(Pool p : this.pools) {
|
||||
pools.add(p.create(lootTableManager));
|
||||
}
|
||||
}
|
||||
return new LootTable(type, pools);
|
||||
}
|
||||
|
||||
private static class Pool {
|
||||
private ConditionContainer[] conditions;
|
||||
private FunctionContainer[] functions;
|
||||
private RangeContainer rolls;
|
||||
private RangeContainer bonus_rools;
|
||||
|
||||
private Entry[] entries;
|
||||
|
||||
private Pool() {}
|
||||
|
||||
public LootTable.Pool create(LootTableManager lootTableManager) {
|
||||
List<LootTable.Entry> entries = new LinkedList<>();
|
||||
List<Condition> conditions = new LinkedList<>();
|
||||
if(this.entries != null) {
|
||||
for (Entry e : this.entries) {
|
||||
entries.add(e.create(lootTableManager));
|
||||
}
|
||||
}
|
||||
if(this.conditions != null) {
|
||||
for (ConditionContainer c : this.conditions) {
|
||||
conditions.add(c.create(lootTableManager));
|
||||
}
|
||||
}
|
||||
if(rolls == null)
|
||||
rolls = new RangeContainer(0,0);
|
||||
if(bonus_rools == null)
|
||||
bonus_rools = new RangeContainer(0,0);
|
||||
return new LootTable.Pool(rolls.getMin(), rolls.getMax(), bonus_rools.getMin(), bonus_rools.getMax(), entries, conditions);
|
||||
}
|
||||
}
|
||||
|
||||
private static class Entry {
|
||||
private ConditionContainer[] conditions;
|
||||
private String type;
|
||||
private String name;
|
||||
private Entry[] children;
|
||||
private boolean expand;
|
||||
private FunctionContainer[] functions;
|
||||
private int weight;
|
||||
private int quality;
|
||||
|
||||
private Entry() {}
|
||||
|
||||
public LootTable.Entry create(LootTableManager lootTableManager) {
|
||||
LootTableEntryType entryType = lootTableManager.getEntryType(NamespaceID.from(type));
|
||||
List<Condition> conditions = new LinkedList<>();
|
||||
if(this.conditions != null) {
|
||||
for(ConditionContainer c : this.conditions) {
|
||||
conditions.add(c.create(lootTableManager));
|
||||
}
|
||||
}
|
||||
List<LootTable.Entry> children = new LinkedList<>();
|
||||
if(this.children != null) {
|
||||
for (Entry c : this.children) {
|
||||
children.add(c.create(lootTableManager));
|
||||
}
|
||||
}
|
||||
List<LootTableFunction> functions = new LinkedList<>();
|
||||
if(this.functions != null) {
|
||||
for(FunctionContainer c : this.functions) {
|
||||
functions.add(c.create(lootTableManager));
|
||||
}
|
||||
}
|
||||
return entryType.create(lootTableManager, name, conditions, children, expand, functions, weight, quality);
|
||||
}
|
||||
}
|
||||
|
||||
private static class FunctionContainer {
|
||||
private String function;
|
||||
private ConditionContainer[] conditions;
|
||||
|
||||
private FunctionContainer() {}
|
||||
|
||||
public LootTableFunction create(LootTableManager lootTableManager) {
|
||||
List<Condition> conditions = new LinkedList<>();
|
||||
if(this.conditions != null) {
|
||||
for(ConditionContainer c : this.conditions) {
|
||||
conditions.add(c.create(lootTableManager));
|
||||
}
|
||||
}
|
||||
return new ConditionedFunctionWrapper(lootTableManager.getFunction(NamespaceID.from(function)), conditions);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables;
|
||||
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@FunctionalInterface
|
||||
public interface LootTableEntryType {
|
||||
LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality);
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
|
||||
/**
|
||||
* Changes to apply to the stack being produced
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface LootTableFunction {
|
||||
|
||||
/**
|
||||
* Applies changes to the stack being produced
|
||||
* @param stack
|
||||
* @param data arguments to pass to the function.
|
||||
* @return
|
||||
*/
|
||||
ItemStack apply(ItemStack stack, Data data);
|
||||
|
||||
LootTableFunction IDENTITY = (stack, _d) -> stack;
|
||||
}
|
||||
|
@ -1,135 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.registry.ResourceGatherer;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
import net.minestom.server.utils.NamespaceIDHashMap;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/**
|
||||
* Handles loading and configuration of loot tables
|
||||
*/
|
||||
public final class LootTableManager {
|
||||
|
||||
private NamespaceIDHashMap<JsonDeserializer<? extends Condition>> conditionDeserializers = new NamespaceIDHashMap<>();
|
||||
private NamespaceIDHashMap<LootTableType> tableTypes = new NamespaceIDHashMap<>();
|
||||
private NamespaceIDHashMap<LootTableEntryType> entryTypes = new NamespaceIDHashMap<>();
|
||||
private NamespaceIDHashMap<LootTableFunction> functions = new NamespaceIDHashMap<>();
|
||||
private NamespaceIDHashMap<LootTable> cache = new NamespaceIDHashMap<>();
|
||||
private Gson gson;
|
||||
|
||||
public LootTableManager() {
|
||||
gson = new GsonBuilder()
|
||||
.registerTypeAdapter(RangeContainer.class, new RangeContainer.Deserializer())
|
||||
.registerTypeAdapter(ConditionContainer.class, new ConditionContainer.Deserializer(this))
|
||||
.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a condition factory to the given namespaceID
|
||||
*
|
||||
* @param namespaceID
|
||||
* @param factory
|
||||
*/
|
||||
public <T extends Condition> void registerConditionDeserializer(NamespaceID namespaceID, JsonDeserializer<T> factory) {
|
||||
conditionDeserializers.put(namespaceID, factory);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a loot table type to the given namespaceID
|
||||
*
|
||||
* @param namespaceID
|
||||
* @param type
|
||||
*/
|
||||
public void registerTableType(NamespaceID namespaceID, LootTableType type) {
|
||||
tableTypes.put(namespaceID, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a loot table entry type to the given namespaceID
|
||||
*
|
||||
* @param namespaceID
|
||||
* @param type
|
||||
*/
|
||||
public void registerEntryType(NamespaceID namespaceID, LootTableEntryType type) {
|
||||
entryTypes.put(namespaceID, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a loot table function to the given namespaceID
|
||||
*
|
||||
* @param namespaceID
|
||||
* @param function
|
||||
*/
|
||||
public void registerFunction(NamespaceID namespaceID, LootTableFunction function) {
|
||||
functions.put(namespaceID, function);
|
||||
}
|
||||
|
||||
public LootTable load(NamespaceID name) throws FileNotFoundException {
|
||||
return load(name, new FileReader(new File(ResourceGatherer.DATA_FOLDER, "data/" + name.getDomain() + "/loot_tables/" + name.getPath() + ".json")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a loot table with the given name. Loot tables can be cached, so 'reader' is used only on cache misses
|
||||
*
|
||||
* @param name the name to cache the loot table with
|
||||
* @param reader the reader to read the loot table from, if none cached. **Will** be closed no matter the results of this call
|
||||
* @return
|
||||
*/
|
||||
public LootTable load(NamespaceID name, Reader reader) {
|
||||
try (reader) {
|
||||
return cache.computeIfAbsent(name, _name -> create(reader));
|
||||
} catch (IOException e) {
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private LootTable create(Reader reader) {
|
||||
LootTableContainer container = gson.fromJson(reader, LootTableContainer.class);
|
||||
return container.createTable(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the registered table type corresponding to the given namespace ID. If none is registered, throws {@link IllegalArgumentException}
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public LootTableType getTableType(NamespaceID id) {
|
||||
if (!tableTypes.containsKey(id))
|
||||
throw new IllegalArgumentException("Unknown table type: " + id);
|
||||
return tableTypes.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the registered entry type corresponding to the given namespace ID. If none is registered, throws {@link IllegalArgumentException}
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public LootTableEntryType getEntryType(NamespaceID id) {
|
||||
if (!entryTypes.containsKey(id))
|
||||
throw new IllegalArgumentException("Unknown entry type: " + id);
|
||||
return entryTypes.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the registered table type corresponding to the given namespace ID. If none is registered, returns {@link LootTableFunction#IDENTITY}
|
||||
*
|
||||
* @param id
|
||||
* @return
|
||||
*/
|
||||
public LootTableFunction getFunction(NamespaceID id) {
|
||||
return functions.getOrDefault(id, LootTableFunction.IDENTITY);
|
||||
}
|
||||
|
||||
public JsonDeserializer<? extends Condition> getConditionDeserializer(NamespaceID id) {
|
||||
return conditionDeserializers.getOrDefault(id, (json, typeOfT, context) -> Condition.ALWAYS_NO);
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables;
|
||||
|
||||
public interface LootTableType {
|
||||
|
||||
// TODO
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables;
|
||||
|
||||
import com.google.gson.*;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class RangeContainer {
|
||||
|
||||
private int min;
|
||||
private int max;
|
||||
|
||||
RangeContainer() {}
|
||||
|
||||
public RangeContainer(int min, int max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public int getMin() {
|
||||
return min;
|
||||
}
|
||||
|
||||
public int getMax() {
|
||||
return max;
|
||||
}
|
||||
|
||||
public static class Deserializer implements JsonDeserializer<RangeContainer> {
|
||||
|
||||
@Override
|
||||
public RangeContainer deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||
RangeContainer range = new RangeContainer();
|
||||
if(json.isJsonPrimitive()) {
|
||||
range.min = json.getAsInt();
|
||||
range.max = json.getAsInt();
|
||||
} else if(json.isJsonObject()) {
|
||||
JsonObject obj = json.getAsJsonObject();
|
||||
if(!obj.has("min"))
|
||||
throw new IllegalArgumentException("Missing 'min' property");
|
||||
if(!obj.has("max"))
|
||||
throw new IllegalArgumentException("Missing 'max' property");
|
||||
range.min = obj.get("min").getAsInt();
|
||||
range.max = obj.get("max").getAsInt();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Range must be single integer or an object with 'min' and 'max' properties");
|
||||
}
|
||||
return range;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AlternativesEntry extends LootTable.Entry {
|
||||
private final List<LootTable.Entry> children;
|
||||
|
||||
public AlternativesEntry(AlternativesType type, List<LootTable.Entry> children, int weight, int quality, List<Condition> conditions) {
|
||||
super(type, weight, quality, conditions);
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(List<ItemStack> output, Data arguments) {
|
||||
for (LootTable.Entry c : children) {
|
||||
int previousSize = output.size();
|
||||
c.generateStacks(output, arguments);
|
||||
int newSize = output.size();
|
||||
if (newSize != previousSize) { // an entry managed to generate, stop here
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableEntryType;
|
||||
import net.minestom.server.gamedata.loottables.LootTableFunction;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* minecraft:alternatives
|
||||
*/
|
||||
public class AlternativesType implements LootTableEntryType {
|
||||
@Override
|
||||
public LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality) {
|
||||
return new AlternativesEntry(this, children, weight, quality, conditions);
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AnotherLootTableEntry extends LootTable.Entry {
|
||||
private final LootTable table;
|
||||
|
||||
public AnotherLootTableEntry(AnotherLootTableType type, LootTable table, int weight, int quality, List<Condition> conditions) {
|
||||
super(type, weight, quality, conditions);
|
||||
this.table = table;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(List<ItemStack> output, Data arguments) {
|
||||
output.addAll(table.generate(arguments));
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableEntryType;
|
||||
import net.minestom.server.gamedata.loottables.LootTableFunction;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Allows to sample from a different loot table
|
||||
*
|
||||
* minecraft:loot_table
|
||||
*/
|
||||
public class AnotherLootTableType implements LootTableEntryType {
|
||||
@Override
|
||||
public LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality) {
|
||||
try {
|
||||
return new AnotherLootTableEntry(this, lootTableManager.load(NamespaceID.from(name)), weight, quality, conditions);
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new IllegalArgumentException(name+" is not a valid loot table name", e);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class DynamicEntry extends LootTable.Entry {
|
||||
|
||||
public static final String DROP_LIST_KEY = "minestom:loot_table_drop_list";
|
||||
|
||||
private final DynamicEntry.Type entryType;
|
||||
|
||||
public DynamicEntry(DynamicType type, DynamicEntry.Type entryType, int weight, int quality, List<Condition> conditions) {
|
||||
super(type, weight, quality, conditions);
|
||||
this.entryType = entryType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(List<ItemStack> output, Data arguments) {
|
||||
List<ItemStack> toDrop = arguments.getOrDefault(DROP_LIST_KEY, Collections.emptyList());
|
||||
output.addAll(toDrop);
|
||||
}
|
||||
|
||||
public DynamicEntry.Type getEntryType() {
|
||||
return entryType;
|
||||
}
|
||||
|
||||
public enum Type {
|
||||
SELF,
|
||||
CONTENTS
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableEntryType;
|
||||
import net.minestom.server.gamedata.loottables.LootTableFunction;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* minecraft:dynamic
|
||||
*/
|
||||
public class DynamicType implements LootTableEntryType {
|
||||
@Override
|
||||
public LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality) {
|
||||
return new DynamicEntry(this, DynamicEntry.Type.valueOf(name.toUpperCase()), weight, quality, conditions);
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GroupEntry extends LootTable.Entry {
|
||||
private final List<LootTable.Entry> children;
|
||||
|
||||
public GroupEntry(GroupType type, List<LootTable.Entry> children, int weight, int quality, List<Condition> conditions) {
|
||||
super(type, weight, quality, conditions);
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(List<ItemStack> output, Data arguments) {
|
||||
for (LootTable.Entry child : children) {
|
||||
child.generateStacks(output, arguments);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableEntryType;
|
||||
import net.minestom.server.gamedata.loottables.LootTableFunction;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* minecraft:group
|
||||
*/
|
||||
public class GroupType implements LootTableEntryType {
|
||||
@Override
|
||||
public LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality) {
|
||||
return new GroupEntry(this, children, weight, quality, conditions);
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableFunction;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
public class ItemEntry extends LootTable.Entry {
|
||||
|
||||
private final List<LootTableFunction> functions;
|
||||
private final Material item;
|
||||
|
||||
ItemEntry(ItemType type, Material baseItem, int weight, int quality, List<LootTableFunction> functions, List<Condition> conditions) {
|
||||
super(type, weight, quality, conditions);
|
||||
this.item = baseItem;
|
||||
this.functions = new LinkedList<>(functions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(List<ItemStack> output, Data arguments) {
|
||||
ItemStack stack = ItemStack.of(item);
|
||||
for (LootTableFunction function : functions) {
|
||||
stack = function.apply(stack, arguments);
|
||||
}
|
||||
if (!stack.isAir()) {
|
||||
output.add(stack);
|
||||
}
|
||||
}
|
||||
|
||||
public List<LootTableFunction> getFunctions() {
|
||||
return functions;
|
||||
}
|
||||
|
||||
public Material getItem() {
|
||||
return item;
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableEntryType;
|
||||
import net.minestom.server.gamedata.loottables.LootTableFunction;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* minecraft:item
|
||||
*/
|
||||
public class ItemType implements LootTableEntryType {
|
||||
@Override
|
||||
public LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality) {
|
||||
NamespaceID itemID = NamespaceID.from(name);
|
||||
return new ItemEntry(this, Material.fromNamespaceId(itemID), weight, quality, functions, conditions);
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SequenceEntry extends LootTable.Entry {
|
||||
private final List<LootTable.Entry> children;
|
||||
|
||||
public SequenceEntry(SequenceType type, List<LootTable.Entry> children, int weight, int quality, List<Condition> conditions) {
|
||||
super(type, weight, quality, conditions);
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(List<ItemStack> output, Data arguments) {
|
||||
for (LootTable.Entry c : children) {
|
||||
int previousSize = output.size();
|
||||
c.generateStacks(output, arguments);
|
||||
int newSize = output.size();
|
||||
if (newSize == previousSize) { // an entry failed to generate, stop here
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableEntryType;
|
||||
import net.minestom.server.gamedata.loottables.LootTableFunction;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* minecraft:sequence
|
||||
*/
|
||||
public class SequenceType implements LootTableEntryType {
|
||||
@Override
|
||||
public LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality) {
|
||||
return new SequenceEntry(this, children, weight, quality, conditions);
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.tags.Tag;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
public class TagEntry extends LootTable.Entry {
|
||||
private final Tag tag;
|
||||
private final boolean expand;
|
||||
private Random rng = new Random();
|
||||
|
||||
TagEntry(TagType type, Tag tag, boolean expand, int weight, int quality, List<Condition> conditions) {
|
||||
super(type, weight, quality, conditions);
|
||||
this.tag = tag;
|
||||
this.expand = expand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(List<ItemStack> output, Data arguments) {
|
||||
Set<NamespaceID> values = tag.getValues();
|
||||
if (values.isEmpty())
|
||||
return;
|
||||
Material[] asArrayOfItems = new Material[values.size()];
|
||||
int ptr = 0;
|
||||
for (NamespaceID id : values) {
|
||||
asArrayOfItems[ptr++] = Material.fromNamespaceId(id);
|
||||
}
|
||||
if (expand) {
|
||||
Material selectedMaterial = asArrayOfItems[rng.nextInt(asArrayOfItems.length)];
|
||||
output.add(ItemStack.of(selectedMaterial));
|
||||
} else {
|
||||
for (Material material : asArrayOfItems) {
|
||||
output.add(ItemStack.of(material));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.entries;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.gamedata.Condition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableEntryType;
|
||||
import net.minestom.server.gamedata.loottables.LootTableFunction;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* minecraft:tag
|
||||
*/
|
||||
public class TagType implements LootTableEntryType {
|
||||
@Override
|
||||
public LootTable.Entry create(LootTableManager lootTableManager, String name, List<Condition> conditions, List<LootTable.Entry> children, boolean expand, List<LootTableFunction> functions, int weight, int quality) {
|
||||
try {
|
||||
return new TagEntry(this, MinecraftServer.getTagManager().load(NamespaceID.from(name), "items"), expand, weight, quality, conditions);
|
||||
} catch (FileNotFoundException e) {
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package net.minestom.server.gamedata.loottables.tabletypes;
|
||||
|
||||
import net.minestom.server.gamedata.loottables.LootTableType;
|
||||
|
||||
public class BlockType implements LootTableType {
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
package net.minestom.server.gamedata.tags;
|
||||
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
|
||||
public class RequiredTag {
|
||||
private final Tag.BasicTypes type;
|
||||
private final NamespaceID name;
|
||||
|
||||
public RequiredTag(Tag.BasicTypes type, NamespaceID name) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public NamespaceID getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Tag.BasicTypes getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
@ -1,32 +1,34 @@
|
||||
package net.minestom.server.gamedata.tags;
|
||||
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.registry.Registries;
|
||||
import net.minestom.server.registry.Registry;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Represents a group of items, blocks, fluids, entity types or function.
|
||||
* Immutable by design
|
||||
*/
|
||||
public class Tag {
|
||||
|
||||
public static final Tag EMPTY = new Tag(NamespaceID.from("minestom:empty"));
|
||||
public final class Tag {
|
||||
private final NamespaceID name;
|
||||
|
||||
private Set<NamespaceID> values;
|
||||
private final Set<NamespaceID> values;
|
||||
|
||||
/**
|
||||
* Creates a new empty tag. This does not cache the tag.
|
||||
*/
|
||||
public Tag(NamespaceID name) {
|
||||
this.name = name;
|
||||
values = new HashSet<>();
|
||||
lockValues();
|
||||
this.values = new HashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,42 +37,10 @@ public class Tag {
|
||||
public Tag(NamespaceID name, Set<NamespaceID> values) {
|
||||
this.name = name;
|
||||
this.values = new HashSet<>(values);
|
||||
lockValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new tag with the contents of the container
|
||||
*
|
||||
* @param manager Used to load tag contents (as tags are valid values inside 'values')
|
||||
* @param lowerPriority Tag contents from lower priority data packs. If 'replace' is false in 'container',
|
||||
* appends the contents of that pack to the one being constructed
|
||||
* @param container
|
||||
*/
|
||||
public Tag(TagManager manager, NamespaceID name, String type, Tag lowerPriority, TagContainer container) throws FileNotFoundException {
|
||||
this.name = name;
|
||||
values = new HashSet<>();
|
||||
if (!container.replace) {
|
||||
values.addAll(lowerPriority.values);
|
||||
}
|
||||
Objects.requireNonNull(container.values, "Attempted to load from a TagContainer with no 'values' array");
|
||||
for (String line : container.values) {
|
||||
if (line.startsWith("#")) { // pull contents from a tag
|
||||
Tag subtag = manager.load(NamespaceID.from(line.substring(1)), type);
|
||||
values.addAll(subtag.values);
|
||||
} else {
|
||||
values.add(NamespaceID.from(line));
|
||||
}
|
||||
}
|
||||
|
||||
lockValues();
|
||||
}
|
||||
|
||||
private void lockValues() {
|
||||
values = Set.copyOf(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given id in inside this tag
|
||||
* Checks whether the given id in inside this tag.
|
||||
*
|
||||
* @param id the id to check against
|
||||
* @return 'true' iif this tag contains the given id
|
||||
@ -85,40 +55,55 @@ public class Tag {
|
||||
* @return immutable set of values present in this tag
|
||||
*/
|
||||
public Set<NamespaceID> getValues() {
|
||||
return values;
|
||||
return Collections.unmodifiableSet(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this tag
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public NamespaceID getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public enum BasicTypes {
|
||||
BLOCKS("minecraft:block"),
|
||||
ITEMS("minecraft:item"),
|
||||
FLUIDS("minecraft:fluid"),
|
||||
ENTITY_TYPES("minecraft:entity_type"),
|
||||
GAME_EVENTS("minecraft:game_event");
|
||||
public enum BasicType {
|
||||
BLOCKS("minecraft:block", Registry.Resource.BLOCK_TAGS,
|
||||
name -> Objects.requireNonNull(Block.fromNamespaceId(name)).id()),
|
||||
ITEMS("minecraft:item", Registry.Resource.ITEM_TAGS,
|
||||
name -> Objects.requireNonNull(Material.fromNamespaceId(name)).id()),
|
||||
FLUIDS("minecraft:fluid", Registry.Resource.FLUID_TAGS,
|
||||
name -> 1), // TODO
|
||||
ENTITY_TYPES("minecraft:entity_type", Registry.Resource.ENTITY_TYPE_TAGS,
|
||||
name -> Objects.requireNonNull(EntityType.fromNamespaceId(name)).id()),
|
||||
GAME_EVENTS("minecraft:game_event", Registry.Resource.GAMEPLAY_TAGS,
|
||||
name -> Registries.getFluid(name).ordinal());
|
||||
|
||||
private final static BasicTypes[] VALUES = values();
|
||||
private final static BasicType[] VALUES = values();
|
||||
private final String identifier;
|
||||
private final Registry.Resource resource;
|
||||
private final Function<String, Integer> function;
|
||||
|
||||
BasicTypes(@NotNull String identifier) {
|
||||
BasicType(@NotNull String identifier,
|
||||
@NotNull Registry.Resource resource,
|
||||
@NotNull Function<String, Integer> function) {
|
||||
this.identifier = identifier;
|
||||
this.resource = resource;
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public String getIdentifier() {
|
||||
public @NotNull String getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static BasicTypes fromIdentifer(@NotNull String identifier) {
|
||||
for (BasicTypes value : VALUES) {
|
||||
public Registry.Resource getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public Function<String, Integer> getFunction() {
|
||||
return function;
|
||||
}
|
||||
|
||||
public static @Nullable Tag.BasicType fromIdentifer(@NotNull String identifier) {
|
||||
for (BasicType value : VALUES) {
|
||||
if (value.identifier.equals(identifier)) {
|
||||
return value;
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
package net.minestom.server.gamedata.tags;
|
||||
|
||||
/**
|
||||
* Meant only for parsing tag JSON
|
||||
*/
|
||||
public class TagContainer {
|
||||
boolean replace;
|
||||
String[] values;
|
||||
}
|
@ -1,340 +1,57 @@
|
||||
package net.minestom.server.gamedata.tags;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.network.packet.server.play.TagsPacket;
|
||||
import net.minestom.server.registry.ResourceGatherer;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minestom.server.registry.Registry;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Handles loading and caching of tags.
|
||||
*/
|
||||
public class TagManager {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(TagManager.class);
|
||||
private final Gson gson;
|
||||
private final Map<NamespaceID, Tag> cache = new ConcurrentHashMap<>();
|
||||
private final List<RequiredTag> requiredTags = new LinkedList<>();
|
||||
public final class TagManager {
|
||||
private final Map<Tag.BasicType, List<Tag>> tagMap = new ConcurrentHashMap<>();
|
||||
|
||||
public TagManager() {
|
||||
gson = new GsonBuilder()
|
||||
.create();
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("acacia_logs"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("anvil"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("bamboo_plantable_on"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("banners"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("base_stone_nether"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("base_stone_overworld"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("beacon_base_blocks"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("beds"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("beehives"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("bee_growables"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("birch_logs"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("buttons"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("campfires"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("candles"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("candle_cakes"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("carpets"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("cauldrons"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("cave_vines"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("climbable"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("coal_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("copper_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("corals"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("coral_blocks"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("coral_plants"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("crimson_stems"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("crops"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("crystal_sound_blocks"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("dark_oak_logs"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("deepslate_ore_replaceables"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("diamond_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("dirt"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("doors"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("dragon_immune"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("dripstone_replaceable_blocks"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("emerald_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("enderman_holdable"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("features_cannot_replace"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("fences"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("fence_gates"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("fire"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("flowers"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("flower_pots"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("geode_invalid_blocks"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("gold_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("guarded_by_piglins"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("hoglin_repellents"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("ice"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("impermeable"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("infiniburn_end"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("infiniburn_nether"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("infiniburn_overworld"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("inside_step_sound_blocks"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("iron_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("jungle_logs"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("lapis_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("lava_pool_stone_replaceables"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("leaves"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("logs"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("logs_that_burn"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("lush_ground_replaceable"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("moss_replaceable"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("mushroom_grow_block"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("needs_diamond_tool"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("needs_iron_tool"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("needs_stone_tool"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("non_flammable_wood"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("nylium"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("oak_logs"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("occludes_vibration_signals"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("piglin_repellents"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("planks"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("portals"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("pressure_plates"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("prevent_mob_spawning_inside"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("rails"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("redstone_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("sand"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("saplings"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("shulker_boxes"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("signs"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("slabs"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("small_dripleaf_placeable"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("small_flowers"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("snow"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("soul_fire_base_blocks"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("soul_speed_blocks"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("spruce_logs"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("stairs"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("standing_signs"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("stone_bricks"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("stone_ore_replaceables"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("stone_pressure_plates"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("strider_warm_blocks"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("tall_flowers"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("trapdoors"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("underwater_bonemeals"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("unstable_bottom_center"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("valid_spawn"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("walls"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("wall_corals"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("wall_post_override"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("wall_signs"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("warped_stems"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("wart_blocks"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("wither_immune"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("wither_summon_base_blocks"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("wooden_buttons"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("wooden_doors"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("wooden_fences"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("wooden_pressure_plates"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("wooden_slabs"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("wooden_stairs"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("wooden_trapdoors"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("wool"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("mineable/axe"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("mineable/hoe"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("mineable/pickaxe"));
|
||||
addRequiredTag(Tag.BasicTypes.BLOCKS, NamespaceID.from("mineable/shovel"));
|
||||
addRequiredTag(Tag.BasicTypes.ENTITY_TYPES, NamespaceID.from("arrows"));
|
||||
addRequiredTag(Tag.BasicTypes.ENTITY_TYPES, NamespaceID.from("axolotl_always_hostiles"));
|
||||
addRequiredTag(Tag.BasicTypes.ENTITY_TYPES, NamespaceID.from("axolotl_hunt_targets"));
|
||||
addRequiredTag(Tag.BasicTypes.ENTITY_TYPES, NamespaceID.from("beehive_inhabitors"));
|
||||
addRequiredTag(Tag.BasicTypes.ENTITY_TYPES, NamespaceID.from("freeze_hurts_extra_types"));
|
||||
addRequiredTag(Tag.BasicTypes.ENTITY_TYPES, NamespaceID.from("freeze_immune_entity_types"));
|
||||
addRequiredTag(Tag.BasicTypes.ENTITY_TYPES, NamespaceID.from("impact_projectiles"));
|
||||
addRequiredTag(Tag.BasicTypes.ENTITY_TYPES, NamespaceID.from("powder_snow_walkable_mobs"));
|
||||
addRequiredTag(Tag.BasicTypes.ENTITY_TYPES, NamespaceID.from("raiders"));
|
||||
addRequiredTag(Tag.BasicTypes.ENTITY_TYPES, NamespaceID.from("skeletons"));
|
||||
addRequiredTag(Tag.BasicTypes.FLUIDS, NamespaceID.from("lava"));
|
||||
addRequiredTag(Tag.BasicTypes.FLUIDS, NamespaceID.from("water"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("acacia_logs"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("anvil"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("arrows"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("axolotl_tempt_items"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("banners"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("beacon_payment_items"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("beds"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("birch_logs"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("boats"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("buttons"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("candles"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("carpets"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("cluster_max_harvestables"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("coals"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("coal_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("copper_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("creeper_drop_music_discs"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("crimson_stems"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("dark_oak_logs"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("diamond_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("doors"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("emerald_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("fences"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("fishes"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("flowers"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("fox_food"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("freeze_immune_wearables"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("gold_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("ignored_by_piglin_babies"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("iron_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("jungle_logs"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("lapis_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("leaves"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("lectern_books"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("logs"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("logs_that_burn"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("music_discs"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("non_flammable_wood"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("oak_logs"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("occludes_vibration_signals"));;
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("piglin_food"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("piglin_loved"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("piglin_repellents"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("planks"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("rails"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("redstone_ores"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("sand"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("saplings"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("signs"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("slabs"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("small_flowers"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("soul_fire_base_blocks"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("spruce_logs"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("stairs"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("stone_bricks"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("stone_crafting_materials"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("stone_tool_materials"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("tall_flowers"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("trapdoors"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("walls"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("warped_stems"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("wooden_buttons"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("wooden_doors"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("wooden_fences"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("wooden_pressure_plates"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("wooden_slabs"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("wooden_stairs"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("wooden_trapdoors"));
|
||||
addRequiredTag(Tag.BasicTypes.ITEMS, NamespaceID.from("wool"));
|
||||
addRequiredTag(Tag.BasicTypes.GAME_EVENTS, NamespaceID.from("ignore_vibrations_sneaking"));
|
||||
addRequiredTag(Tag.BasicTypes.GAME_EVENTS, NamespaceID.from("vibrations"));
|
||||
// Load required tags from files
|
||||
for (var type : Tag.BasicType.values()) {
|
||||
final var json = Registry.load(type.getResource());
|
||||
final var tagIdentifierMap = tagMap.computeIfAbsent(type, s -> new CopyOnWriteArrayList<>());
|
||||
json.keySet().forEach(tagName -> {
|
||||
final var tag = new Tag(NamespaceID.from(tagName), getValues(json, tagName));
|
||||
tagIdentifierMap.add(tag);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a tag with the given name. This method attempts to read from "data/<name.domain>/tags/<tagType>/<name.path>.json" if the given name is not already present in cache
|
||||
*
|
||||
* @param name
|
||||
* @param tagType the type of the tag to load, used to resolve paths (blocks, items, entity_types, fluids, functions are the vanilla variants)
|
||||
* @return
|
||||
* @throws FileNotFoundException if the file does not exist
|
||||
*/
|
||||
public Tag load(NamespaceID name, String tagType) throws FileNotFoundException {
|
||||
return load(name, tagType, () -> new FileReader(new File(ResourceGatherer.DATA_FOLDER, "data/" + name.getDomain() + "/tags/" + tagType + "/" + name.getPath() + ".json")));
|
||||
public @Nullable Tag getTag(Tag.BasicType type, String namespace) {
|
||||
final var tags = tagMap.get(type);
|
||||
for (var tag : tags) {
|
||||
if (tag.getName().asString().equals(namespace))
|
||||
return tag;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a tag with the given name. This method attempts to read from 'reader' if the given name is not already present in cache
|
||||
*
|
||||
* @param name
|
||||
* @param tagType the type of the tag to load, used to resolve paths (blocks, items, entity_types, fluids, functions are the vanilla variants)
|
||||
* @param reader
|
||||
* @return
|
||||
*/
|
||||
public Tag load(NamespaceID name, String tagType, Reader reader) throws FileNotFoundException {
|
||||
return load(name, tagType, () -> reader);
|
||||
public Map<Tag.BasicType, List<Tag>> getTagMap() {
|
||||
return Collections.unmodifiableMap(tagMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a tag with the given name. This method reads from 'reader'. This will override the previous tag
|
||||
*
|
||||
* @param name
|
||||
* @param tagType the type of the tag to load, used to resolve paths (blocks, items, entity_types, fluids, functions are the vanilla variants)
|
||||
* @param readerSupplier
|
||||
* @return
|
||||
*/
|
||||
public Tag forceLoad(NamespaceID name, String tagType, ReaderSupplierWithFileNotFound readerSupplier) throws FileNotFoundException {
|
||||
Tag prev = cache.getOrDefault(name, Tag.EMPTY);
|
||||
Tag result = create(prev, name, tagType, readerSupplier);
|
||||
cache.put(name, result);
|
||||
private Set<NamespaceID> getValues(JsonObject main, String value) {
|
||||
JsonObject tagObject = main.getAsJsonObject(value);
|
||||
final var tagValues = tagObject.getAsJsonArray("values");
|
||||
Set<NamespaceID> result = new HashSet<>(tagValues.size());
|
||||
tagValues.forEach(jsonElement -> {
|
||||
final String tagString = jsonElement.getAsString();
|
||||
if (tagString.startsWith("#")) {
|
||||
result.addAll(getValues(main, tagString.substring(1)));
|
||||
} else {
|
||||
result.add(NamespaceID.from(tagString));
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a tag with the given name. This method attempts to read from 'reader' if the given name is not already present in cache
|
||||
*
|
||||
* @param name
|
||||
* @param tagType the type of the tag to load, used to resolve paths (blocks, items, entity_types, fluids, functions are the vanilla variants)
|
||||
* @param readerSupplier
|
||||
* @return
|
||||
*/
|
||||
public Tag load(NamespaceID name, String tagType, ReaderSupplierWithFileNotFound readerSupplier) throws FileNotFoundException {
|
||||
Tag prev = cache.getOrDefault(name, Tag.EMPTY);
|
||||
Tag result = cache.get(name);
|
||||
if (result == null) {
|
||||
result = create(prev, name, tagType, readerSupplier);
|
||||
cache.put(name, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Tag create(Tag prev, NamespaceID name, String tagType, ReaderSupplierWithFileNotFound reader) throws FileNotFoundException {
|
||||
TagContainer container = gson.fromJson(reader.get(), TagContainer.class);
|
||||
try {
|
||||
return new Tag(this, name, tagType, prev, container);
|
||||
} catch (FileNotFoundException e) {
|
||||
LOGGER.error("Failed to load tag due to error", e);
|
||||
return Tag.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the required tags for the game to function correctly
|
||||
*
|
||||
* @param tagsPacket the packet to add the tags to
|
||||
*/
|
||||
public void addRequiredTagsToPacket(TagsPacket tagsPacket) {
|
||||
for (RequiredTag requiredTag : requiredTags) {
|
||||
final Tag tag = silentLoad(requiredTag.getName(), requiredTag.getType().name().toLowerCase());
|
||||
var map = tagsPacket.tagsMap.computeIfAbsent(requiredTag.getType(), s -> new ArrayList<>());
|
||||
map.add(tag);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a required tag to send to players when they connect
|
||||
*
|
||||
* @param type type of tag to send. Required so the client knows its use
|
||||
* @param name the name of the tag to load
|
||||
*/
|
||||
public void addRequiredTag(Tag.BasicTypes type, NamespaceID name) {
|
||||
requiredTags.add(new RequiredTag(type, name));
|
||||
}
|
||||
|
||||
private Tag silentLoad(NamespaceID name, String type) {
|
||||
try {
|
||||
return load(name, type);
|
||||
} catch (FileNotFoundException e) {
|
||||
MinecraftServer.getExceptionManager().handleException(e);
|
||||
return Tag.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
public interface ReaderSupplierWithFileNotFound {
|
||||
Reader get() throws FileNotFoundException;
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +1,28 @@
|
||||
package net.minestom.server.network.packet.server.play;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.entity.EntityType;
|
||||
import net.minestom.server.gamedata.tags.Tag;
|
||||
import net.minestom.server.instance.block.Block;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.network.packet.server.ServerPacket;
|
||||
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
|
||||
import net.minestom.server.registry.Registries;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
import net.minestom.server.utils.binary.BinaryReader;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class TagsPacket implements ServerPacket {
|
||||
private static final TagsPacket REQUIRED_TAGS_PACKET = new TagsPacket(MinecraftServer.getTagManager().getTagMap());
|
||||
|
||||
private static final TagsPacket REQUIRED_TAGS_PACKET = new TagsPacket();
|
||||
public Map<Tag.BasicType, List<Tag>> tagsMap;
|
||||
|
||||
static {
|
||||
MinecraftServer.getTagManager().addRequiredTagsToPacket(REQUIRED_TAGS_PACKET);
|
||||
public TagsPacket(Map<Tag.BasicType, List<Tag>> tagsMap) {
|
||||
this.tagsMap = tagsMap;
|
||||
}
|
||||
|
||||
public Map<Tag.BasicTypes, List<Tag>> tagsMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Default constructor, required for reflection operations.
|
||||
*/
|
||||
public TagsPacket() {
|
||||
this(new HashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -37,105 +31,14 @@ public class TagsPacket implements ServerPacket {
|
||||
for (var entry : tagsMap.entrySet()) {
|
||||
final var type = entry.getKey();
|
||||
final var tags = entry.getValue();
|
||||
// Tag type
|
||||
writer.writeSizedString(type.getIdentifier());
|
||||
switch (type) {
|
||||
case BLOCKS: {
|
||||
// Number of tags
|
||||
writer.writeVarInt(tags.size());
|
||||
for (Tag tag : tags) {
|
||||
// name
|
||||
writer.writeSizedString(tag.getName().toString());
|
||||
final Set<NamespaceID> values = tag.getValues();
|
||||
// count
|
||||
for (var tag : tags) {
|
||||
writer.writeSizedString(tag.getName().asString());
|
||||
final var values = tag.getValues();
|
||||
writer.writeVarInt(values.size());
|
||||
// entries
|
||||
for (NamespaceID name : values) {
|
||||
Block b = Block.fromNamespaceId(name);
|
||||
if (b == null) {
|
||||
writer.writeVarInt(-1);
|
||||
continue;
|
||||
}
|
||||
writer.writeVarInt(b.id());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ENTITY_TYPES: {
|
||||
// Number of tags
|
||||
writer.writeVarInt(tags.size());
|
||||
for (Tag tag : tags) {
|
||||
// name
|
||||
writer.writeSizedString(tag.getName().toString());
|
||||
|
||||
final Set<NamespaceID> values = tag.getValues();
|
||||
// count
|
||||
writer.writeVarInt(values.size());
|
||||
// entries
|
||||
for (NamespaceID name : values) {
|
||||
EntityType et = EntityType.fromNamespaceId(name);
|
||||
if (et == null) {
|
||||
writer.writeVarInt(-1);
|
||||
} else {
|
||||
writer.writeVarInt(et.id());
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FLUIDS: {
|
||||
// Number of tags
|
||||
writer.writeVarInt(tags.size());
|
||||
for (Tag tag : tags) {
|
||||
// name
|
||||
writer.writeSizedString(tag.getName().toString());
|
||||
|
||||
final Set<NamespaceID> values = tag.getValues();
|
||||
// count
|
||||
writer.writeVarInt(values.size());
|
||||
// entries
|
||||
for (NamespaceID name : values) {
|
||||
writer.writeVarInt(Registries.getFluid(name).ordinal());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GAME_EVENTS: {
|
||||
// Number of tags
|
||||
writer.writeVarInt(tags.size());
|
||||
for (Tag tag : tags) {
|
||||
// name
|
||||
writer.writeSizedString(tag.getName().toString());
|
||||
|
||||
final Set<NamespaceID> values = tag.getValues();
|
||||
// count
|
||||
writer.writeVarInt(values.size());
|
||||
// entries
|
||||
for (NamespaceID name : values) {
|
||||
// TODO: GameEvents
|
||||
writer.writeVarInt(-1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ITEMS: {
|
||||
// Number of tags
|
||||
writer.writeVarInt(tags.size());
|
||||
for (Tag tag : tags) {
|
||||
// name
|
||||
writer.writeSizedString(tag.getName().toString());
|
||||
|
||||
final Set<NamespaceID> values = tag.getValues();
|
||||
// count
|
||||
writer.writeVarInt(values.size());
|
||||
// entries
|
||||
for (NamespaceID name : values) {
|
||||
// FIXME: invalid namespace
|
||||
final var material = Objects.requireNonNullElse(Material.fromNamespaceId(name), Material.AIR);
|
||||
writer.writeVarInt(material.id());
|
||||
}
|
||||
}
|
||||
break;
|
||||
for (var name : values) {
|
||||
writer.writeVarInt(type.getFunction().apply(name.asString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -148,7 +51,7 @@ public class TagsPacket implements ServerPacket {
|
||||
final int typeCount = reader.readVarInt();
|
||||
for (int i = 0; i < typeCount; i++) {
|
||||
// Read tag type
|
||||
final Tag.BasicTypes tagType = Tag.BasicTypes.fromIdentifer(reader.readSizedString());
|
||||
final Tag.BasicType tagType = Tag.BasicType.fromIdentifer(reader.readSizedString());
|
||||
if (tagType == null) {
|
||||
throw new IllegalArgumentException("Tag type could not be resolved");
|
||||
}
|
||||
@ -173,8 +76,7 @@ public class TagsPacket implements ServerPacket {
|
||||
*
|
||||
* @return the default tags packet
|
||||
*/
|
||||
@NotNull
|
||||
public static TagsPacket getRequiredTagsPacket() {
|
||||
public static @NotNull TagsPacket getRequiredTagsPacket() {
|
||||
return REQUIRED_TAGS_PACKET;
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,13 @@ public class Registry {
|
||||
STATISTICS("custom_statistics"),
|
||||
POTION_EFFECTS("potion_effects"),
|
||||
POTION_TYPES("potions"),
|
||||
PARTICLES("particles");
|
||||
PARTICLES("particles"),
|
||||
|
||||
BLOCK_TAGS("tags/block_tags"),
|
||||
ENTITY_TYPE_TAGS("tags/entity_type_tags"),
|
||||
FLUID_TAGS("tags/fluid_tags"),
|
||||
GAMEPLAY_TAGS("tags/gameplay_tags"),
|
||||
ITEM_TAGS("tags/item_tags");
|
||||
|
||||
private final String name;
|
||||
|
||||
|
@ -1,216 +0,0 @@
|
||||
package net.minestom.server.registry;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import it.unimi.dsi.fastutil.io.FastBufferedInputStream;
|
||||
import net.minestom.server.utils.StringUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
/**
|
||||
* Responsible for making sure Minestom has the necessary files to run (notably registry files)
|
||||
*/
|
||||
public class ResourceGatherer {
|
||||
public static final File DATA_FOLDER = new File("./minecraft_data/");
|
||||
private static final Gson GSON = new Gson();
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ResourceGatherer.class);
|
||||
private static final File TMP_FOLDER = new File("./.minestom_tmp/");
|
||||
|
||||
/**
|
||||
* Checks if registry/ folder is present
|
||||
* If it is not, download the minecraft server jar, run the data generator and extract the wanted files
|
||||
* If it is already present, directly return
|
||||
*/
|
||||
public static void ensureResourcesArePresent(String version) throws IOException {
|
||||
if (DATA_FOLDER.exists()) {
|
||||
return;
|
||||
}
|
||||
LOGGER.info("{} folder does not exist. Minestom will now generate the necessary files.", DATA_FOLDER);
|
||||
|
||||
if (!TMP_FOLDER.exists() && !TMP_FOLDER.mkdirs()) {
|
||||
throw new IOException("Failed to create tmp folder.");
|
||||
}
|
||||
|
||||
LOGGER.info("Starting download of Minecraft server jar for version {} from Mojang servers...", version);
|
||||
File serverJar = downloadServerJar(version);
|
||||
LOGGER.info("Download complete.");
|
||||
|
||||
runDataGenerator(serverJar);
|
||||
|
||||
moveAndCleanup(version);
|
||||
LOGGER.info("Resource gathering done!");
|
||||
}
|
||||
|
||||
private static void moveAndCleanup(String version) throws IOException {
|
||||
Path dataFolderPath = DATA_FOLDER.toPath();
|
||||
Path tmpFolderPath = TMP_FOLDER.toPath();
|
||||
Path generatedFolder = tmpFolderPath.resolve("generated");
|
||||
LOGGER.info("Data generator successful, removing server jar");
|
||||
Files.delete(tmpFolderPath.resolve("server_" + version + ".jar"));
|
||||
LOGGER.info("Removal successful, now moving data to {}", DATA_FOLDER);
|
||||
Files.walkFileTree(tmpFolderPath, new SimpleFileVisitor<>() {
|
||||
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
|
||||
Path relativePath = generatedFolder.relativize(dir);
|
||||
if (dir.startsWith(generatedFolder)) { // don't copy logs
|
||||
Path resolvedPath = dataFolderPath.resolve(relativePath);
|
||||
LOGGER.info("> Creating sub-folder {}", resolvedPath);
|
||||
Files.createDirectories(resolvedPath);
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||
LOGGER.info("> Deleting folder {}", dir);
|
||||
Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
Path relativePath = generatedFolder.relativize(file);
|
||||
if (file.startsWith(generatedFolder)) { // don't copy logs
|
||||
Path resolvedPath = dataFolderPath.resolve(relativePath);
|
||||
LOGGER.info("> Moving {}", relativePath);
|
||||
Files.move(file, resolvedPath);
|
||||
} else {
|
||||
LOGGER.info("> Deleting {}", relativePath);
|
||||
Files.delete(file);
|
||||
}
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void runDataGenerator(File serverJar) throws IOException {
|
||||
final String javaExecutable = System.getProperty("java.home") + "/bin/java";
|
||||
ProcessBuilder dataGenerator = new ProcessBuilder(javaExecutable, "-cp", serverJar.getName(), "net.minecraft.data.Main", "--all", "--server", "--dev");
|
||||
dataGenerator.directory(TMP_FOLDER);
|
||||
LOGGER.info("Now running data generator with options '--dev', '--server', '--all'");
|
||||
LOGGER.info("Executing: {}", String.join(StringUtils.SPACE, dataGenerator.command()));
|
||||
LOGGER.info("Minestom will now wait for it to finish, here's its output:");
|
||||
LOGGER.info("");
|
||||
Process dataGeneratorProcess = dataGenerator.start();
|
||||
new BufferedReader(
|
||||
new InputStreamReader(dataGeneratorProcess.getInputStream())
|
||||
).lines().forEach(LOGGER::info);
|
||||
new BufferedReader(
|
||||
new InputStreamReader(dataGeneratorProcess.getErrorStream())
|
||||
).lines().forEach(LOGGER::error);
|
||||
LOGGER.info("");
|
||||
|
||||
try {
|
||||
int resultCode = dataGeneratorProcess.waitFor();
|
||||
if (resultCode != 0) {
|
||||
throw new IOException("Data generator finished with non-zero return code " + resultCode + " verify that you have 'java' cli");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new IOException("Data generator was interrupted.", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static File downloadServerJar(String version) throws IOException {
|
||||
// Mojang's version manifest is located at https://launchermeta.mojang.com/mc/game/version_manifest.json
|
||||
// If we query this (it's a json object), we can then search for the id we want.
|
||||
InputStream versionManifestStream = new URL("https://launchermeta.mojang.com/mc/game/version_manifest.json").openStream();
|
||||
LOGGER.debug("Successfully queried Mojang's version_manifest.json.");
|
||||
|
||||
JsonObject versionManifestJson = GSON.fromJson(new InputStreamReader(versionManifestStream), JsonObject.class);
|
||||
LOGGER.debug("Successfully read Mojang's version_manifest.json into a json object.");
|
||||
|
||||
JsonArray versionArray = versionManifestJson.getAsJsonArray("versions");
|
||||
LOGGER.debug("Iterating over the version manifest to find a version with the id {}.", version);
|
||||
|
||||
JsonObject versionEntry = null;
|
||||
for (JsonElement element : versionArray) {
|
||||
if (element.isJsonObject()) {
|
||||
JsonObject entry = element.getAsJsonObject();
|
||||
if (entry.get("id").getAsString().equals(version)) {
|
||||
LOGGER.debug("Successfully found a version with the id {}.", version);
|
||||
versionEntry = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (versionEntry == null) {
|
||||
throw new IOException("Could not find " + version + " in Mojang's official list of minecraft versions.");
|
||||
}
|
||||
// We now have the entry we want and it gives us access to the json file containing the downloads.
|
||||
String versionUrl = versionEntry.get("url").getAsString();
|
||||
InputStream versionStream = new URL(versionUrl).openStream();
|
||||
LOGGER.debug("Successfully queried {}.json.", version);
|
||||
|
||||
JsonObject versionJson = GSON.fromJson(new InputStreamReader(versionStream), JsonObject.class);
|
||||
LOGGER.debug("Successfully read {}.json into a json object.", version);
|
||||
|
||||
// Now we need to navigate to "downloads.client.url" and "downloads.server.url" }
|
||||
JsonObject downloadsJson = versionJson.getAsJsonObject("downloads");
|
||||
|
||||
// Designated spot if we ever need the client.
|
||||
|
||||
// Server
|
||||
{
|
||||
JsonObject serverJson = downloadsJson.getAsJsonObject("server");
|
||||
final String jarURL = serverJson.get("url").getAsString();
|
||||
final String sha1 = serverJson.get("sha1").getAsString();
|
||||
|
||||
LOGGER.debug("Found all information required to download the server JAR file.");
|
||||
LOGGER.debug("Attempting download.");
|
||||
return download(version, jarURL, sha1);
|
||||
}
|
||||
}
|
||||
|
||||
private static File download(@NotNull String version, @NotNull String url, @NotNull String sha1Source) throws IOException {
|
||||
File target = new File(TMP_FOLDER, "server_" + version + ".jar");
|
||||
// Download
|
||||
try (FastBufferedInputStream in = new FastBufferedInputStream(new URL(url).openStream())) {
|
||||
Files.copy(in, target.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Failed to download Minecraft server jar.", e);
|
||||
}
|
||||
// Verify checksum
|
||||
try (FileInputStream fis = new FileInputStream(target)) {
|
||||
MessageDigest messageDigest = MessageDigest.getInstance("SHA-1");
|
||||
messageDigest.reset();
|
||||
messageDigest.update(fis.readAllBytes());
|
||||
byte[] digest = messageDigest.digest();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (byte b : digest) {
|
||||
sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
|
||||
}
|
||||
// This just converts the sha1 back into a readable string.
|
||||
String sha1Target = sb.toString();
|
||||
if (!sha1Target.equals(sha1Source)) {
|
||||
LOGGER.error("The checksum test failed after downloading the Minecraft server jar.");
|
||||
LOGGER.error("The expected checksum was: {}.", sha1Source);
|
||||
LOGGER.error("The calculated checksum was: {}.", sha1Target);
|
||||
throw new IOException("Failed to download Minecraft server jar.");
|
||||
}
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
LOGGER.error("Failed to find SHA-1 hashing algorithm in Java Environment.");
|
||||
throw new IOException("Failed to download Minecraft server jar.");
|
||||
}
|
||||
return target;
|
||||
}
|
||||
}
|
@ -1,149 +0,0 @@
|
||||
package loottables;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.data.Data;
|
||||
import net.minestom.server.data.DataImpl;
|
||||
import net.minestom.server.gamedata.conditions.SurvivesExplosionCondition;
|
||||
import net.minestom.server.gamedata.loottables.LootTable;
|
||||
import net.minestom.server.gamedata.loottables.LootTableManager;
|
||||
import net.minestom.server.gamedata.loottables.entries.ItemEntry;
|
||||
import net.minestom.server.gamedata.loottables.entries.ItemType;
|
||||
import net.minestom.server.gamedata.loottables.tabletypes.BlockType;
|
||||
import net.minestom.server.item.ItemStack;
|
||||
import net.minestom.server.item.Material;
|
||||
import net.minestom.server.registry.ResourceGatherer;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
|
||||
public class TestLootTables {
|
||||
|
||||
private LootTableManager tableManager;
|
||||
|
||||
@BeforeEach
|
||||
public void init() {
|
||||
try {
|
||||
ResourceGatherer.ensureResourcesArePresent(MinecraftServer.VERSION_NAME);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
tableManager = new LootTableManager();
|
||||
tableManager.registerConditionDeserializer(NamespaceID.from("minecraft:survives_explosion"), new SurvivesExplosionCondition.Deserializer());
|
||||
tableManager.registerTableType(NamespaceID.from("minecraft:block"), new BlockType());
|
||||
tableManager.registerEntryType(NamespaceID.from("minecraft:item"), new ItemType());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadFromString() {
|
||||
// from acacia_button.json
|
||||
final String lootTableJson = "{\n" +
|
||||
" \"type\": \"minecraft:block\",\n" +
|
||||
" \"pools\": [\n" +
|
||||
" {\n" +
|
||||
" \"rolls\": 1,\n" +
|
||||
" \"entries\": [\n" +
|
||||
" {\n" +
|
||||
" \"type\": \"minecraft:item\",\n" +
|
||||
" \"name\": \"minecraft:acacia_button\"\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"conditions\": [\n" +
|
||||
" {\n" +
|
||||
" \"condition\": \"minecraft:survives_explosion\"\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
"}";
|
||||
LootTable lootTable = tableManager.load(NamespaceID.from("blocks/acacia_button"), new StringReader(lootTableJson));
|
||||
Assertions.assertTrue(lootTable.getType() instanceof BlockType);
|
||||
Assertions.assertEquals(1, lootTable.getPools().size());
|
||||
Assertions.assertEquals(1, lootTable.getPools().get(0).getMinRollCount());
|
||||
Assertions.assertEquals(1, lootTable.getPools().get(0).getMaxRollCount());
|
||||
Assertions.assertEquals(1, lootTable.getPools().get(0).getEntries().size());
|
||||
Assertions.assertTrue(lootTable.getPools().get(0).getEntries().get(0).getType() instanceof ItemType);
|
||||
Assertions.assertTrue(lootTable.getPools().get(0).getEntries().get(0) instanceof ItemEntry);
|
||||
ItemEntry entry = (ItemEntry) lootTable.getPools().get(0).getEntries().get(0);
|
||||
Assertions.assertEquals(Material.ACACIA_BUTTON, entry.getItem());
|
||||
Assertions.assertEquals(0, entry.getFunctions().size());
|
||||
Assertions.assertEquals(1, lootTable.getPools().get(0).getConditions().size());
|
||||
Assertions.assertTrue(lootTable.getPools().get(0).getConditions().get(0) instanceof SurvivesExplosionCondition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadFromFile() throws FileNotFoundException {
|
||||
LootTable lootTable = tableManager.load(NamespaceID.from("blocks/acacia_button"));
|
||||
Assertions.assertTrue(lootTable.getType() instanceof BlockType);
|
||||
Assertions.assertEquals(1, lootTable.getPools().size());
|
||||
Assertions.assertEquals(1, lootTable.getPools().get(0).getMinRollCount());
|
||||
Assertions.assertEquals(1, lootTable.getPools().get(0).getMaxRollCount());
|
||||
Assertions.assertEquals(1, lootTable.getPools().get(0).getEntries().size());
|
||||
Assertions.assertTrue(lootTable.getPools().get(0).getEntries().get(0).getType() instanceof ItemType);
|
||||
Assertions.assertTrue(lootTable.getPools().get(0).getEntries().get(0) instanceof ItemEntry);
|
||||
ItemEntry entry = (ItemEntry) lootTable.getPools().get(0).getEntries().get(0);
|
||||
Assertions.assertEquals(Material.ACACIA_BUTTON, entry.getItem());
|
||||
Assertions.assertEquals(0, entry.getFunctions().size());
|
||||
Assertions.assertEquals(1, lootTable.getPools().get(0).getConditions().size());
|
||||
Assertions.assertTrue(lootTable.getPools().get(0).getConditions().get(0) instanceof SurvivesExplosionCondition);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void caching() throws FileNotFoundException {
|
||||
LootTable lootTable1 = tableManager.load(NamespaceID.from("blocks/acacia_button"));
|
||||
LootTable lootTable2 = tableManager.load(NamespaceID.from("blocks/acacia_button"));
|
||||
Assertions.assertSame(lootTable1, lootTable2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleGenerate() throws FileNotFoundException {
|
||||
LootTable lootTable = tableManager.load(NamespaceID.from("blocks/acacia_button"));
|
||||
Data arguments = new DataImpl();
|
||||
List<ItemStack> stacks = lootTable.generate(arguments);
|
||||
Assertions.assertEquals(1, stacks.size());
|
||||
Assertions.assertEquals(Material.ACACIA_BUTTON, stacks.get(0).getMaterial());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExplosion() throws FileNotFoundException {
|
||||
LootTable lootTable = tableManager.load(NamespaceID.from("blocks/acacia_button"));
|
||||
Data arguments = new DataImpl();
|
||||
// negative value will force the condition to fail
|
||||
arguments.set("explosionPower", -1.0, Double.class);
|
||||
List<ItemStack> stacks = lootTable.generate(arguments);
|
||||
Assertions.assertEquals(0, stacks.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unknownCondition() {
|
||||
// from acacia_button.json
|
||||
final String lootTableJson = "{\n" +
|
||||
" \"type\": \"minecraft:block\",\n" +
|
||||
" \"pools\": [\n" +
|
||||
" {\n" +
|
||||
" \"rolls\": 1,\n" +
|
||||
" \"entries\": [\n" +
|
||||
" {\n" +
|
||||
" \"type\": \"minecraft:item\",\n" +
|
||||
" \"name\": \"minecraft:acacia_button\"\n" +
|
||||
" }\n" +
|
||||
" ],\n" +
|
||||
" \"conditions\": [\n" +
|
||||
" {\n" +
|
||||
" \"condition\": \"minestom:unknown\"\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
" }\n" +
|
||||
" ]\n" +
|
||||
"}";
|
||||
LootTable lootTable = tableManager.load(NamespaceID.from("blocks/none"), new StringReader(lootTableJson));
|
||||
List<ItemStack> stacks = lootTable.generate(Data.EMPTY);
|
||||
Assertions.assertEquals(0, stacks.size());
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
package tags;
|
||||
|
||||
import net.minestom.server.gamedata.tags.Tag;
|
||||
import net.minestom.server.gamedata.tags.TagManager;
|
||||
import net.minestom.server.utils.NamespaceID;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.StringReader;
|
||||
|
||||
public class TestTags {
|
||||
|
||||
private TagManager tags;
|
||||
|
||||
@BeforeEach
|
||||
public void init() {
|
||||
tags = new TagManager();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubTag() throws FileNotFoundException {
|
||||
String tag1 = "{\n" +
|
||||
"\t\"replace\": false,\n" +
|
||||
"\t\"values\": [\n" +
|
||||
"\t\t\"minestom:an_item\"\n" +
|
||||
"\t]\n" +
|
||||
"}";
|
||||
|
||||
String tag2 = "{\n" +
|
||||
"\t\"replace\": false,\n" +
|
||||
"\t\"values\": [\n" +
|
||||
"\t\t\"#minestom:test_sub\",\n" +
|
||||
"\t\t\"minestom:some_other_item\"\n" +
|
||||
"\t]\n" +
|
||||
"}";
|
||||
Assertions.assertNotEquals(Tag.EMPTY, tags.load(NamespaceID.from("minestom:test_sub"), "any", new StringReader(tag1)));
|
||||
Tag loaded = tags.load(NamespaceID.from("minestom:test"), "any", new StringReader(tag2));
|
||||
NamespaceID[] values = loaded.getValues().toArray(new NamespaceID[0]);
|
||||
Assertions.assertEquals(2, values.length);
|
||||
Assertions.assertTrue(loaded.contains(NamespaceID.from("minestom:an_item")));
|
||||
Assertions.assertTrue(loaded.contains(NamespaceID.from("minestom:some_other_item")));
|
||||
Assertions.assertFalse(loaded.contains(NamespaceID.from("minestom:some_other_item_that_is_not_in_the_tag")));
|
||||
}
|
||||
|
||||
/**
|
||||
* A value of 'true' in 'replace' should replace previous contents
|
||||
*/
|
||||
@Test
|
||||
public void testReplacement() throws FileNotFoundException {
|
||||
String tag1 = "{\n" +
|
||||
"\t\"replace\": false,\n" +
|
||||
"\t\"values\": [\n" +
|
||||
"\t\t\"minestom:an_item\"\n" +
|
||||
"\t]\n" +
|
||||
"}";
|
||||
|
||||
String tag2 = "{\n" +
|
||||
"\t\"replace\": true,\n" +
|
||||
"\t\"values\": [\n" +
|
||||
"\t\t\"minestom:some_other_item\"\n" +
|
||||
"\t]\n" +
|
||||
"}";
|
||||
Assertions.assertNotEquals(Tag.EMPTY, tags.load(NamespaceID.from("minestom:test"), "any", new StringReader(tag1)));
|
||||
Tag loaded = tags.forceLoad(NamespaceID.from("minestom:test"), "any", () -> new StringReader(tag2));
|
||||
Assertions.assertNotEquals(Tag.EMPTY, loaded);
|
||||
Assertions.assertEquals(1, loaded.getValues().size());
|
||||
Assertions.assertTrue(loaded.contains(NamespaceID.from("minestom:some_other_item")));
|
||||
Assertions.assertFalse(loaded.contains(NamespaceID.from("minestom:an_item")));
|
||||
}
|
||||
|
||||
/**
|
||||
* A value of 'false' in 'replace' should append to previous contents
|
||||
*/
|
||||
@Test
|
||||
public void testAppend() throws FileNotFoundException {
|
||||
String tag1 = "{\n" +
|
||||
"\t\"replace\": false,\n" +
|
||||
"\t\"values\": [\n" +
|
||||
"\t\t\"minestom:an_item\"\n" +
|
||||
"\t]\n" +
|
||||
"}";
|
||||
|
||||
String tag2 = "{\n" +
|
||||
"\t\"replace\": false,\n" +
|
||||
"\t\"values\": [\n" +
|
||||
"\t\t\"minestom:some_other_item\"\n" +
|
||||
"\t]\n" +
|
||||
"}";
|
||||
Assertions.assertNotEquals(Tag.EMPTY, tags.load(NamespaceID.from("minestom:test"), "any", new StringReader(tag1)));
|
||||
Tag loaded = tags.forceLoad(NamespaceID.from("minestom:test"), "any", () -> new StringReader(tag2));
|
||||
Assertions.assertNotEquals(Tag.EMPTY, loaded);
|
||||
Assertions.assertEquals(2, loaded.getValues().size());
|
||||
Assertions.assertTrue(loaded.contains(NamespaceID.from("minestom:some_other_item")));
|
||||
Assertions.assertTrue(loaded.contains(NamespaceID.from("minestom:an_item")));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void cleanup() {
|
||||
tags = null;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user