mirror of https://github.com/Minestom/Minestom.git
Initial TagDatabase api
Signed-off-by: TheMode <themode@outlook.fr>
This commit is contained in:
parent
7320437640
commit
f9b4f788e3
|
@ -0,0 +1,104 @@
|
|||
package net.minestom.server.tag;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
@ApiStatus.Experimental
|
||||
public sealed interface TagDatabase permits TagDatabaseImpl {
|
||||
static @NotNull TagDatabase database() {
|
||||
return new TagDatabaseImpl();
|
||||
}
|
||||
|
||||
@NotNull TagHandler newHandler();
|
||||
|
||||
@NotNull Selection select(@NotNull Condition condition);
|
||||
|
||||
@NotNull Selection selectAll();
|
||||
|
||||
<T> void track(Tag<T> tag, BiConsumer<TagHandler, T> consumer);
|
||||
|
||||
default <T> @NotNull Optional<TagHandler> findFirst(@NotNull Tag<T> tag, @NotNull T value) {
|
||||
final Selection selection = select(Condition.eq(tag, value));
|
||||
final List<TagHandler> collect = selection.collect();
|
||||
return collect.isEmpty() ? Optional.empty() : Optional.of(collect.get(0));
|
||||
}
|
||||
|
||||
sealed interface Selection permits TagDatabaseImpl.SelectionImpl {
|
||||
void operate(@NotNull List<@NotNull Operation> operations);
|
||||
|
||||
default void operate(@NotNull Operation @NotNull ... operations) {
|
||||
operate(List.of(operations));
|
||||
}
|
||||
|
||||
@NotNull List<@NotNull TagHandler> collect(Map<Tag<?>, SortOrder> sorters, int limit);
|
||||
|
||||
default @NotNull List<@NotNull TagHandler> collect() {
|
||||
return collect(Map.of(), -1);
|
||||
}
|
||||
|
||||
void deleteAll();
|
||||
}
|
||||
|
||||
sealed interface Condition permits Condition.And, Condition.Eq, Condition.Range {
|
||||
static @NotNull Condition and(@NotNull Condition left, @NotNull Condition right) {
|
||||
return new TagDatabaseImpl.ConditionAnd(left, right);
|
||||
}
|
||||
|
||||
static <T> @NotNull Condition eq(@NotNull Tag<T> tag, @NotNull T value) {
|
||||
return new TagDatabaseImpl.ConditionEq<>(tag, value);
|
||||
}
|
||||
|
||||
static <T extends Number> @NotNull Condition range(@NotNull Tag<T> tag, @NotNull T min, @NotNull T max) {
|
||||
return new TagDatabaseImpl.ConditionRange<>(tag, min, max);
|
||||
}
|
||||
|
||||
sealed interface And extends Condition permits TagDatabaseImpl.ConditionAnd {
|
||||
@NotNull Condition left();
|
||||
|
||||
@NotNull Condition right();
|
||||
}
|
||||
|
||||
sealed interface Eq<T> extends Condition permits TagDatabaseImpl.ConditionEq {
|
||||
@NotNull Tag<T> tag();
|
||||
|
||||
@NotNull T value();
|
||||
}
|
||||
|
||||
sealed interface Range<T extends Number> extends Condition permits TagDatabaseImpl.ConditionRange {
|
||||
@NotNull Tag<T> tag();
|
||||
|
||||
@NotNull T min();
|
||||
|
||||
@NotNull T max();
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface Operation permits Operation.Set {
|
||||
static <T> Operation set(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
return new TagDatabaseImpl.OperationSet<>(tag, value);
|
||||
}
|
||||
|
||||
sealed interface Set<T> extends Operation permits TagDatabaseImpl.OperationSet {
|
||||
@NotNull Tag<T> tag();
|
||||
|
||||
@Nullable T value();
|
||||
}
|
||||
}
|
||||
|
||||
sealed interface Sorter permits TagDatabaseImpl.Sorter {
|
||||
@NotNull Tag<?> tag();
|
||||
|
||||
@NotNull SortOrder sortOrder();
|
||||
}
|
||||
|
||||
enum SortOrder {
|
||||
ASCENDING,
|
||||
DESCENDING
|
||||
}
|
||||
}
|
|
@ -0,0 +1,227 @@
|
|||
package net.minestom.server.tag;
|
||||
|
||||
import it.unimi.dsi.fastutil.Pair;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompound;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBTCompoundLike;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
final class TagDatabaseImpl implements TagDatabase {
|
||||
private final ReentrantLock lock = new ReentrantLock();
|
||||
private final List<Entry> entries = new ArrayList<>();
|
||||
private final Map<String, List<Pair<Tag, BiConsumer<TagHandler, Object>>>> tracked = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public @NotNull TagHandler newHandler() {
|
||||
final Entry entry = new Entry();
|
||||
this.lock.lock();
|
||||
this.entries.add(entry);
|
||||
this.lock.unlock();
|
||||
return entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Selection select(@NotNull Condition condition) {
|
||||
return new SelectionImpl(condition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Selection selectAll() {
|
||||
return new SelectionImpl(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void track(Tag<T> tag, BiConsumer<TagHandler, T> consumer) {
|
||||
Pair pair = Pair.of(tag, (BiConsumer<TagHandler, Object>) consumer);
|
||||
lock.lock();
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (tag.path != null) {
|
||||
for (Tag.PathEntry s : tag.path) {
|
||||
builder.append(s.name());
|
||||
tracked.computeIfAbsent(builder.toString(), tmp -> new ArrayList<>()).add(pair);
|
||||
builder.append('.');
|
||||
}
|
||||
}
|
||||
builder.append(tag.getKey());
|
||||
tracked.computeIfAbsent(builder.toString(), tmp -> new ArrayList<>()).add(pair);
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
private static String getTagPath(Tag<?> tag) {
|
||||
// Create string like test.test.test from tag path + name
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (tag.path != null) {
|
||||
for (Tag.PathEntry s : tag.path) {
|
||||
builder.append(s.name()).append('.');
|
||||
}
|
||||
}
|
||||
builder.append(tag.getKey());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
record ConditionAnd(Condition left, Condition right) implements Condition.And {
|
||||
}
|
||||
|
||||
record ConditionEq<T>(Tag<T> tag, T value) implements Condition.Eq<T> {
|
||||
}
|
||||
|
||||
record ConditionRange<T extends Number>(Tag<T> tag, T min, T max) implements Condition.Range<T> {
|
||||
}
|
||||
|
||||
record OperationSet<T>(Tag<T> tag, T value) implements Operation.Set<T> {
|
||||
}
|
||||
|
||||
record Sorter(Tag<?> tag, SortOrder sortOrder) implements TagDatabase.Sorter {
|
||||
}
|
||||
|
||||
final class SelectionImpl implements Selection {
|
||||
private final Condition condition;
|
||||
|
||||
SelectionImpl(Condition condition) {
|
||||
this.condition = condition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void operate(@NotNull List<@NotNull Operation> operations) {
|
||||
List<TagHandler> collect = collect();
|
||||
for (TagHandler entry : collect) {
|
||||
for (Operation operation : operations) {
|
||||
if (operation instanceof Operation.Set set) {
|
||||
entry.setTag(set.tag(), set.value());
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported: " + operation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull List<@NotNull TagHandler> collect(Map<Tag<?>, SortOrder> sorters, int limit) {
|
||||
List<TagHandler> result = new ArrayList<>();
|
||||
// Insert valid entries
|
||||
lock.lock();
|
||||
for (Entry entry : TagDatabaseImpl.this.entries) {
|
||||
if (condition == null || validate(entry, condition)) {
|
||||
result.add(entry);
|
||||
if (limit > -1 && result.size() == limit) break;
|
||||
}
|
||||
}
|
||||
lock.unlock();
|
||||
// Sort entries
|
||||
if (!sorters.isEmpty()) {
|
||||
Comparator<TagHandler> comparator = null;
|
||||
for (var entry : sorters.entrySet()) {
|
||||
final Tag<?> tag = entry.getKey();
|
||||
final SortOrder sorter = entry.getValue();
|
||||
Comparator<TagHandler> test = Comparator.comparing(tagHandler ->
|
||||
(Comparable) tagHandler.getTag(tag));
|
||||
if (sorter == SortOrder.DESCENDING) {
|
||||
test = test.reversed();
|
||||
}
|
||||
comparator = comparator != null ?
|
||||
comparator.thenComparing(test) : test;
|
||||
}
|
||||
result.sort(comparator);
|
||||
}
|
||||
return List.copyOf(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAll() {
|
||||
lock.lock();
|
||||
TagDatabaseImpl.this.entries.removeIf(entry -> validate(entry, condition));
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
private boolean validate(Entry entry, Condition condition) {
|
||||
if (condition instanceof Condition.Eq<?> eq) {
|
||||
final Object value = entry.getTag(eq.tag());
|
||||
return Objects.equals(value, eq.value());
|
||||
} else {
|
||||
throw new RuntimeException("Unsupported: " + condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class Entry implements TagHandler {
|
||||
private final TagHandler handler = TagHandler.newHandler();
|
||||
|
||||
@Override
|
||||
public @NotNull TagReadable readableCopy() {
|
||||
return handler.readableCopy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TagHandler copy() {
|
||||
return handler.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateContent(@NotNull NBTCompoundLike compound) {
|
||||
this.handler.updateContent(compound);
|
||||
// TODO update?
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull NBTCompound asCompound() {
|
||||
return handler.asCompound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void updateTag(@NotNull Tag<T> tag, @NotNull UnaryOperator<@UnknownNullability T> value) {
|
||||
this.handler.updateTag(tag, value);
|
||||
handleUpdate(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T updateAndGetTag(@NotNull Tag<T> tag, @NotNull UnaryOperator<@UnknownNullability T> value) {
|
||||
final T result = handler.updateAndGetTag(tag, value);
|
||||
handleUpdate(tag);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getAndUpdateTag(@NotNull Tag<T> tag, @NotNull UnaryOperator<@UnknownNullability T> value) {
|
||||
final T result = handler.getAndUpdateTag(tag, value);
|
||||
handleUpdate(tag);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @UnknownNullability T getTag(@NotNull Tag<T> tag) {
|
||||
return handler.getTag(tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> void setTag(@NotNull Tag<T> tag, @Nullable T value) {
|
||||
this.handler.setTag(tag, value);
|
||||
handleUpdate(tag);
|
||||
}
|
||||
|
||||
private void handleUpdate(Tag<?> writeTag) {
|
||||
final String tagPath = getTagPath(writeTag);
|
||||
final String[] split = tagPath.split("\\.");
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String s : split) {
|
||||
builder.append(s);
|
||||
final List<Pair<Tag, BiConsumer<TagHandler, Object>>> pairs = tracked.get(builder.toString());
|
||||
if (pairs != null) {
|
||||
for(var pair : pairs){
|
||||
final Tag tag = pair.left();
|
||||
final Object value = handler.getTag(tag);
|
||||
pair.right().accept(this, value);
|
||||
}
|
||||
}
|
||||
builder.append('.');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,400 @@
|
|||
package net.minestom.server.tag;
|
||||
|
||||
import net.minestom.server.coordinate.Pos;
|
||||
import net.minestom.server.tag.TagDatabase.Condition;
|
||||
import net.minestom.server.tag.TagDatabase.Operation;
|
||||
import org.jglrxavpok.hephaistos.nbt.NBT;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
public class TagDatabaseTest {
|
||||
|
||||
@Test
|
||||
public void insert() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var compound = NBT.Compound(Map.of("key", NBT.Int(1)));
|
||||
db.newHandler().updateContent(compound);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertNested() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var compound = NBT.Compound(Map.of("key",
|
||||
NBT.Compound(Map.of("value", NBT.Int(1)))));
|
||||
db.newHandler().updateContent(compound);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void empty() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var select = db.select(Condition.eq(Tag.String("key"), "value"));
|
||||
var result = select.collect();
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findFilterEq() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var tag = Tag.String("key");
|
||||
|
||||
{
|
||||
db.newHandler().setTag(tag, "value");
|
||||
var collect = db.select(Condition.eq(tag, "value")).collect();
|
||||
assertEquals(1, collect.size());
|
||||
}
|
||||
|
||||
{
|
||||
db.newHandler().setTag(tag, "value");
|
||||
var collect = db.select(Condition.eq(tag, "value")).collect();
|
||||
assertEquals(2, collect.size());
|
||||
}
|
||||
|
||||
{
|
||||
db.newHandler().setTag(tag, "value2");
|
||||
var collect = db.select(Condition.eq(tag, "value")).collect();
|
||||
assertEquals(2, collect.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findFilterCompoundEq() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var child = NBT.Compound(Map.of("something", NBT.String("something")));
|
||||
var compound = NBT.Compound(Map.of("key", NBT.String("value2"),
|
||||
"other", child));
|
||||
|
||||
db.newHandler().updateContent(compound);
|
||||
|
||||
var result = db.select(Condition.eq(Tag.NBT("other"), child)).collect();
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(compound, result.get(0).asCompound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findTagMismatch() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var tagInteger = Tag.Integer("key");
|
||||
var tagDouble = Tag.Double("key");
|
||||
|
||||
db.newHandler().updateContent(NBT.Compound(Map.of("key", NBT.Int(1))));
|
||||
db.newHandler().updateContent(NBT.Compound(Map.of("key", NBT.Double(1))));
|
||||
|
||||
var queryInteger = db.select(Condition.eq(tagInteger, 1));
|
||||
assertEquals(1, queryInteger.collect().size());
|
||||
|
||||
var queryDouble = db.select(Condition.eq(tagDouble, 1D));
|
||||
assertEquals(1, queryDouble.collect().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findArray() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var tag = Tag.NBT("key");
|
||||
var nbt = NBT.IntArray(1, 2, 3);
|
||||
var compound = NBT.Compound(Map.of("key", nbt));
|
||||
|
||||
db.newHandler().updateContent(compound);
|
||||
|
||||
var query = db.select(Condition.eq(tag, nbt));
|
||||
var result = query.collect();
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(compound, result.get(0).asCompound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void valueChange() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var tag = Tag.Integer("key");
|
||||
|
||||
var handler = db.newHandler();
|
||||
handler.setTag(tag, 1);
|
||||
handler.setTag(tag, 5);
|
||||
|
||||
var result1 = db.select(Condition.eq(tag, 1)).collect();
|
||||
var result5 = db.select(Condition.eq(tag, 5)).collect();
|
||||
|
||||
assertEquals(0, result1.size());
|
||||
assertEquals(1, result5.size());
|
||||
assertEquals(handler.asCompound(), result5.get(0).asCompound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findNestedTag() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var handler = db.newHandler();
|
||||
|
||||
var tag = Tag.String("key");
|
||||
var tag2 = Tag.String("key2").path("path");
|
||||
var tag3 = Tag.String("key3").path("path", "path2");
|
||||
var tag4 = Tag.String("key4").path("path", "path2");
|
||||
var tag5 = Tag.String("key4").path("path", "path2", "path3", "path4", "path5");
|
||||
|
||||
handler.setTag(tag, "value");
|
||||
handler.setTag(tag2, "value2");
|
||||
handler.setTag(tag3, "value3");
|
||||
handler.setTag(tag4, "value4");
|
||||
handler.setTag(tag5, "value5");
|
||||
|
||||
var copy = handler.copy();
|
||||
|
||||
// Check query based on nested tag
|
||||
assertListEqualsIgnoreOrder(List.of(copy), db.select(Condition.eq(tag, "value")).collect());
|
||||
assertListEqualsIgnoreOrder(List.of(copy), db.select(Condition.eq(tag2, "value2")).collect());
|
||||
assertListEqualsIgnoreOrder(List.of(copy), db.select(Condition.eq(tag3, "value3")).collect());
|
||||
assertListEqualsIgnoreOrder(List.of(copy), db.select(Condition.eq(tag4, "value4")).collect());
|
||||
assertListEqualsIgnoreOrder(List.of(copy), db.select(Condition.eq(tag5, "value5")).collect());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findFirst() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var tag = Tag.String("key");
|
||||
var tag2 = Tag.String("key2");
|
||||
var handler = db.newHandler();
|
||||
handler.setTag(tag, "value");
|
||||
handler.setTag(tag2, "value2");
|
||||
var copy = handler.copy();
|
||||
|
||||
var result = db.findFirst(tag, "value").orElseThrow();
|
||||
assertEquals(copy.asCompound(), result.asCompound());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replaceConstant() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var tag = Tag.Integer("number");
|
||||
var compound = NBT.Compound(Map.of("number", NBT.Int(5)));
|
||||
|
||||
db.newHandler().updateContent(compound);
|
||||
db.selectAll().operate(Operation.set(tag, 10));
|
||||
|
||||
var result = db.selectAll().collect();
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(10, result.get(0).getTag(tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replaceNull() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var tag = Tag.Integer("number");
|
||||
var compound = NBT.Compound(Map.of("number", NBT.Int(5)));
|
||||
|
||||
db.newHandler().updateContent(compound);
|
||||
db.selectAll().operate(Operation.set(tag, null));
|
||||
|
||||
assertFalse(db.selectAll().collect().isEmpty());
|
||||
assertTrue(db.select(Condition.eq(tag, 5)).collect().isEmpty());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replaceOperator() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var tag = Tag.Integer("number");
|
||||
|
||||
db.newHandler().setTag(tag, 5);
|
||||
db.selectAll().collect().forEach(tagHandler -> tagHandler.updateTag(tag, integer -> integer * 2));
|
||||
|
||||
var result = db.selectAll().collect();
|
||||
assertEquals(1, result.size());
|
||||
assertEquals(10, result.get(0).getTag(tag));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void delete() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var tag = Tag.Integer("number");
|
||||
var compound = NBT.Compound(Map.of("number", NBT.Int(5)));
|
||||
var condition = Condition.eq(tag, 5);
|
||||
|
||||
db.newHandler().updateContent(compound);
|
||||
db.select(condition).deleteAll();
|
||||
|
||||
var result = db.select(condition).collect();
|
||||
assertTrue(result.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void intSort() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var tag = Tag.Integer("number");
|
||||
|
||||
var handler2 = db.newHandler();
|
||||
var handler3 = db.newHandler();
|
||||
var handler1 = db.newHandler();
|
||||
|
||||
handler1.updateContent(NBT.Compound(Map.of("number", NBT.Int(1))));
|
||||
handler2.updateContent(NBT.Compound(Map.of("number", NBT.Int(2))));
|
||||
handler3.updateContent(NBT.Compound(Map.of("number", NBT.Int(3))));
|
||||
|
||||
var ascending = db.selectAll().collect(Map.of(tag, TagDatabase.SortOrder.ASCENDING), -1);
|
||||
assertEquals(List.of(handler1, handler2, handler3), ascending);
|
||||
|
||||
var descending = db.selectAll().collect(Map.of(tag, TagDatabase.SortOrder.DESCENDING), -1);
|
||||
assertEquals(List.of(handler3, handler2, handler1), descending);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nestedSort() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var tag = Tag.Integer("number").path("path", "path2");
|
||||
|
||||
var handler = db.newHandler();
|
||||
var handler2 = db.newHandler();
|
||||
var handler3 = db.newHandler();
|
||||
var handler4 = db.newHandler();
|
||||
|
||||
handler.setTag(tag, 1);
|
||||
handler2.setTag(tag, 2);
|
||||
handler3.setTag(tag, 3);
|
||||
handler4.setTag(tag, 4);
|
||||
|
||||
var ascending = db.selectAll().collect(Map.of(tag, TagDatabase.SortOrder.ASCENDING), -1);
|
||||
assertEquals(List.of(handler, handler2, handler3, handler4), ascending);
|
||||
|
||||
var descending = db.selectAll().collect(Map.of(tag, TagDatabase.SortOrder.DESCENDING), -1);
|
||||
assertEquals(List.of(handler4, handler3, handler2, handler), descending);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tableDownsize() {
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var tag = Tag.Integer("number");
|
||||
var condition = Condition.eq(tag, 1);
|
||||
var selectQuery = db.select(condition);
|
||||
|
||||
var handler = db.newHandler();
|
||||
handler.setTag(tag, 1);
|
||||
|
||||
assertEquals(1, selectQuery.collect().size());
|
||||
|
||||
handler.removeTag(tag);
|
||||
assertEquals(0, selectQuery.collect().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void entityQuery() {
|
||||
var pos = Tag.Structure("pos", Pos.class);
|
||||
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var entity1 = db.newHandler();
|
||||
var entity2 = db.newHandler();
|
||||
|
||||
// Set positions
|
||||
entity1.setTag(pos, new Pos(1, 55, 2));
|
||||
entity2.setTag(pos, new Pos(4, 55, 6));
|
||||
|
||||
// Query entities within a radius of 5 blocks from (0, 55, 0)
|
||||
var condition = Condition.and(
|
||||
Condition.range(Tag.Double("x").path("pos"), -5d, 5d),
|
||||
Condition.range(Tag.Double("z").path("pos"), -5d, 5d)
|
||||
);
|
||||
var entities = db.select(condition).collect();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trackRoot() {
|
||||
var tag = Tag.Integer("value");
|
||||
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var entity = db.newHandler();
|
||||
|
||||
entity.setTag(tag, 1);
|
||||
|
||||
AtomicReference<Integer> ref = new AtomicReference<>(null);
|
||||
db.track(tag, (tagHandler, value) -> {
|
||||
assertNull(ref.get());
|
||||
ref.set(value);
|
||||
});
|
||||
|
||||
entity.setTag(tag, 2);
|
||||
assertEquals(2, ref.get());
|
||||
|
||||
ref.set(null);
|
||||
entity.setTag(tag, 3);
|
||||
assertEquals(3, ref.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trackStruct() {
|
||||
var posTag = Tag.Structure("value", Pos.class);
|
||||
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var entity = db.newHandler();
|
||||
|
||||
entity.setTag(posTag, new Pos(1, 1, 1));
|
||||
|
||||
AtomicReference<Pos> ref = new AtomicReference<>(null);
|
||||
db.track(posTag, (tagHandler, value) -> {
|
||||
assertNull(ref.get());
|
||||
ref.set(value);
|
||||
});
|
||||
|
||||
entity.setTag(posTag, new Pos(2, 2, 2));
|
||||
assertEquals(new Pos(2, 2, 2), ref.get());
|
||||
|
||||
ref.set(null);
|
||||
entity.setTag(posTag, new Pos(3, 3, 3));
|
||||
assertEquals(new Pos(3, 3, 3), ref.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trackUp() {
|
||||
var posTag = Tag.Structure("value", Pos.class);
|
||||
var xTag = Tag.Double("x").path("value");
|
||||
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var entity = db.newHandler();
|
||||
|
||||
entity.setTag(posTag, new Pos(1, 1, 1));
|
||||
|
||||
AtomicReference<Double> ref = new AtomicReference<>(null);
|
||||
db.track(xTag, (tagHandler, value) -> {
|
||||
assertNull(ref.get());
|
||||
ref.set(value);
|
||||
});
|
||||
|
||||
entity.setTag(posTag, new Pos(2, 2, 2));
|
||||
assertEquals(2d, ref.get());
|
||||
|
||||
ref.set(null);
|
||||
entity.setTag(posTag, new Pos(3, 3, 3));
|
||||
assertEquals(3, ref.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trackDown() {
|
||||
var posTag = Tag.Structure("value", Pos.class);
|
||||
var xTag = Tag.Double("x").path("value");
|
||||
|
||||
TagDatabase db = TagDatabase.database();
|
||||
var entity = db.newHandler();
|
||||
|
||||
entity.setTag(posTag, new Pos(1, 1, 1));
|
||||
|
||||
AtomicReference<Pos> ref = new AtomicReference<>(null);
|
||||
db.track(posTag, (tagHandler, value) -> {
|
||||
assertNull(ref.get());
|
||||
ref.set(value);
|
||||
});
|
||||
|
||||
entity.setTag(xTag, 2d);
|
||||
assertEquals(new Pos(2, 1, 1), ref.get());
|
||||
|
||||
ref.set(null);
|
||||
entity.setTag(xTag, 3d);
|
||||
assertEquals(new Pos(3, 1, 1), ref.get());
|
||||
}
|
||||
|
||||
public static void assertListEqualsIgnoreOrder(List<TagHandler> expected, List<TagHandler> actual) {
|
||||
var expectedCompound = expected.stream().map(TagHandler::asCompound).toList();
|
||||
var actualCompound = actual.stream().map(TagHandler::asCompound).toList();
|
||||
assertEquals(new HashSet<>(expectedCompound), new HashSet<>(actualCompound));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue