Merge branch 'tags'

This commit is contained in:
jglrxavpok 2020-06-23 18:17:35 +02:00
commit f79cfa8d87
5 changed files with 295 additions and 0 deletions

View File

@ -6,6 +6,7 @@ import net.minestom.server.data.DataManager;
import net.minestom.server.entity.EntityManager;
import net.minestom.server.entity.Player;
import net.minestom.server.gamedata.loottables.LootTableManager;
import net.minestom.server.gamedata.tags.TagManager;
import net.minestom.server.instance.InstanceManager;
import net.minestom.server.instance.block.BlockManager;
import net.minestom.server.listener.manager.PacketListenerManager;
@ -91,6 +92,7 @@ public class MinecraftServer {
private static ResponseDataConsumer responseDataConsumer;
private static Difficulty difficulty = Difficulty.NORMAL;
private static LootTableManager lootTableManager;
private static TagManager tagManager;
public static MinecraftServer init() {
connectionManager = new ConnectionManager();
@ -111,6 +113,7 @@ public class MinecraftServer {
updateManager = new UpdateManager();
lootTableManager = new LootTableManager();
tagManager = new TagManager();
nettyServer = new NettyServer(packetProcessor);
@ -208,6 +211,10 @@ public class MinecraftServer {
return lootTableManager;
}
public static TagManager getTagManager() {
return tagManager;
}
public void start(String address, int port, ResponseDataConsumer responseDataConsumer) {
LOGGER.info("Starting Minestom server.");
MinecraftServer.responseDataConsumer = responseDataConsumer;

View File

@ -0,0 +1,73 @@
package net.minestom.server.gamedata.tags;
import net.minestom.server.utils.NamespaceID;
import java.io.FileNotFoundException;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
/**
* Represents a group of items, blocks, fluids, entity types or function.
* Immutable by design
*/
public class Tag {
public static final Tag EMPTY = new Tag();
private Set<NamespaceID> values;
/**
* Creates a new empty tag
*/
public Tag() {
values = new HashSet<>();
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, String type, Tag lowerPriority, TagContainer container) throws FileNotFoundException {
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
* @param id the id to check against
* @return 'true' iif this tag contains the given id
*/
public boolean contains(NamespaceID id) {
return values.contains(id);
}
/**
* Returns an immutable set of values present in this tag
* @return immutable set of values present in this tag
*/
public Set<NamespaceID> getValues() {
return values;
}
}

View File

@ -0,0 +1,9 @@
package net.minestom.server.gamedata.tags;
/**
* Meant only for parsing tag JSON
*/
public class TagContainer {
boolean replace;
String[] values;
}

View File

@ -0,0 +1,102 @@
package net.minestom.server.gamedata.tags;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import net.minestom.server.registry.ResourceGatherer;
import net.minestom.server.utils.NamespaceID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
/**
* Handles loading and caching of tags
*/
public class TagManager {
private static final Logger LOGGER = LoggerFactory.getLogger(TagManager.class);
private final Gson gson;
private Map<NamespaceID, Tag> cache = new HashMap<>();
public TagManager() {
gson = new GsonBuilder()
.create();
}
/**
* Loads a tag with the given name. This method attempts to read from "data/&lt;name.domain&gt;/tags/&lt;tagType&gt;/&lt;name.path&gt;.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")));
}
/**
* 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);
}
/**
* 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);
FileNotFoundException[] ex = new FileNotFoundException[1]; // very ugly code but Java does not let its standard interfaces throw exceptions
Tag result = create(prev, tagType, readerSupplier);
cache.put(name, result);
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);
FileNotFoundException[] ex = new FileNotFoundException[1]; // very ugly code but Java does not let its standard interfaces throw exceptions
Tag result = cache.computeIfAbsent(name, _name -> {
try {
return create(prev, tagType, readerSupplier);
} catch (FileNotFoundException e) {
ex[0] = e;
return Tag.EMPTY;
}
});
if(ex[0] != null) {
throw ex[0];
}
return result;
}
private Tag create(Tag prev, String tagType, ReaderSupplierWithFileNotFound reader) throws FileNotFoundException {
TagContainer container = gson.fromJson(reader.get(), TagContainer.class);
try {
return new Tag(this, tagType, prev, container);
} catch (FileNotFoundException e) {
LOGGER.error("Failed to load tag due to error", e);
return Tag.EMPTY;
}
}
public interface ReaderSupplierWithFileNotFound {
Reader get() throws FileNotFoundException;
}
}

View File

@ -0,0 +1,104 @@
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.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.FileNotFoundException;
import java.io.StringReader;
public class TestTags {
private TagManager tags;
@Before
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" +
"}";
Assert.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]);
Assert.assertEquals(2, values.length);
Assert.assertTrue(loaded.contains(NamespaceID.from("minestom:an_item")));
Assert.assertTrue(loaded.contains(NamespaceID.from("minestom:some_other_item")));
Assert.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" +
"}";
Assert.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));
Assert.assertNotEquals(Tag.EMPTY, loaded);
Assert.assertEquals(1, loaded.getValues().size());
Assert.assertTrue(loaded.contains(NamespaceID.from("minestom:some_other_item")));
Assert.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" +
"}";
Assert.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));
Assert.assertNotEquals(Tag.EMPTY, loaded);
Assert.assertEquals(2, loaded.getValues().size());
Assert.assertTrue(loaded.contains(NamespaceID.from("minestom:some_other_item")));
Assert.assertTrue(loaded.contains(NamespaceID.from("minestom:an_item")));
}
@After
public void cleanup() {
tags = null;
}
}