More datahandler work

This commit is contained in:
TomTom 2024-07-22 18:09:44 +02:00
parent e5c2557586
commit 01effe18b1
9 changed files with 157 additions and 34 deletions

View File

@ -12,6 +12,7 @@ import com.artillexstudios.axminions.database.DataHandler;
import com.artillexstudios.axminions.database.DatabaseConnector;
import com.artillexstudios.axminions.listeners.BlockPlaceListener;
import com.artillexstudios.axminions.listeners.ChunkListener;
import com.artillexstudios.axminions.listeners.MinionPlaceListener;
import com.artillexstudios.axminions.listeners.PlayerListener;
import com.artillexstudios.axminions.listeners.WorldListener;
import com.artillexstudios.axminions.minions.MinionTicker;
@ -64,6 +65,7 @@ public final class AxMinionsPlugin extends AxPlugin {
Bukkit.getPluginManager().registerEvents(new ChunkListener(), this);
Bukkit.getPluginManager().registerEvents(new PlayerListener(), this);
Bukkit.getPluginManager().registerEvents(new BlockPlaceListener(), this);
Bukkit.getPluginManager().registerEvents(new MinionPlaceListener(), this);
if (PaperUtils.isFolia()) {
ticker = new FoliaMinionTicker();

View File

@ -1,5 +1,6 @@
package com.artillexstudios.axminions.command;
import com.artillexstudios.axapi.utils.ContainerUtils;
import com.artillexstudios.axapi.utils.MessageUtils;
import com.artillexstudios.axminions.AxMinionsPlugin;
import com.artillexstudios.axminions.command.arguments.MinionLevelArgument;
@ -14,6 +15,7 @@ import com.artillexstudios.axminions.minions.MinionArea;
import com.artillexstudios.axminions.minions.MinionData;
import com.artillexstudios.axminions.minions.MinionType;
import com.artillexstudios.axminions.minions.MinionWorldCache;
import com.artillexstudios.axminions.utils.Direction;
import com.artillexstudios.axminions.utils.FileUtils;
import com.artillexstudios.axminions.utils.LocationUtils;
import dev.jorel.commandapi.CommandTree;
@ -44,7 +46,13 @@ public final class AxMinionsCommand {
.then(MinionTypeArgument.minionType("miniontype")
.then(MinionLevelArgument.level("level")
.then(new IntegerArgument("amount")
.executes((sender, args) -> {
Player player = args.getByClass("player", Player.class);
MinionType type = args.getByClass("miniontype", MinionType.class);
Level level = args.getByClass("level", Level.class);
Integer amount = args.getByClass("amount", Integer.class);
handleGive(sender, player, type, level, amount);
})
)
)
)
@ -102,7 +110,7 @@ public final class AxMinionsCommand {
MinionType type = args.getByClass("miniontype", MinionType.class);
Level level = args.getByClass("level", Level.class);
Location location = LocationUtils.toBlockCenter(sender.getLocation());
MinionData data = new MinionData(0, type, null, level, new ItemStack(Material.DIAMOND_PICKAXE), null, new HashMap<>());
MinionData data = new MinionData(0, type, Direction.NORTH, null, level, 0, new ItemStack(Material.DIAMOND_PICKAXE), null, new HashMap<>());
Minion minion = new Minion(location, data);
minion.spawn();
MinionArea area = MinionWorldCache.getArea(location.getWorld());
@ -117,6 +125,8 @@ public final class AxMinionsCommand {
}
private static void handleGive(CommandSender sender, Player player, MinionType type, Level level, Integer amount) {
ItemStack item = type.item(new MinionData(0, type, null, null, level, 0, null, null, new HashMap<>(0)));
item.setAmount(amount);
ContainerUtils.INSTANCE.addOrDrop(player.getInventory(), List.of(item), player.getLocation());
}
}

View File

@ -1,8 +1,10 @@
package com.artillexstudios.axminions.database;
import com.artillexstudios.axapi.items.WrappedItemStack;
import com.artillexstudios.axapi.nms.NMSHandlers;
import com.artillexstudios.axminions.config.Config;
import com.artillexstudios.axminions.minions.Minion;
import com.artillexstudios.axminions.minions.MinionData;
import com.artillexstudios.axminions.minions.MinionType;
import com.artillexstudios.axminions.utils.LogUtils;
import com.google.common.base.Preconditions;
@ -13,13 +15,11 @@ import org.jetbrains.annotations.NotNull;
import org.jooq.Record;
import org.jooq.Record1;
import org.jooq.Result;
import org.jooq.impl.DSL;
import org.jooq.impl.SQLDataType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutorService;
@ -97,7 +97,7 @@ public final class DataHandler {
futures.add(locations.toCompletableFuture());
CompletionStage<Integer> minions = DatabaseConnector.getInstance().context().createTableIfNotExists(Tables.LOCATIONS)
CompletionStage<Integer> minions = DatabaseConnector.getInstance().context().createTableIfNotExists(Tables.MINIONS)
.column(Fields.ID, SQLDataType.INTEGER.identity(true))
.column(Fields.LOCATION_ID, SQLDataType.INTEGER)
.column(Fields.OWNER_ID, SQLDataType.INTEGER)
@ -160,7 +160,7 @@ public final class DataHandler {
}
Record1<Integer> insert = DatabaseConnector.getInstance().context()
.insertInto(Tables.TYPES)
.insertInto(Tables.WORLDS)
.set(Fields.WORLD_UUID, world.getUID())
.returningResult(Fields.ID)
.fetchOne();
@ -172,22 +172,6 @@ public final class DataHandler {
return insert.get(Fields.ID);
}
public static int ownerId(Player player) {
Result<Record> select = DatabaseConnector.getInstance().context()
.select()
.from(Tables.USERS)
.where(Fields.UUID.eq(player.getUniqueId()))
.fetch();
if (!select.isEmpty()) {
Record record = select.get(0);
LogUtils.debug("World select record: {}", record);
return (Integer) record.get("ID");
}
return FAILED_QUERY;
}
public static int locationId(int world, Location location) {
Result<Record> select = DatabaseConnector.getInstance().context()
.select()
@ -205,7 +189,7 @@ public final class DataHandler {
}
Record1<Integer> insert = DatabaseConnector.getInstance().context()
.insertInto(Tables.TYPES)
.insertInto(Tables.LOCATIONS)
.set(Fields.WORLD_ID, world)
.set(Fields.LOCATION_X, location.getBlockX())
.set(Fields.LOCATION_Y, location.getBlockY())
@ -242,7 +226,13 @@ public final class DataHandler {
DatabaseConnector.getInstance().context()
.insertInto(Tables.MINIONS)
.set(Fields.LOCATION_ID, locationId)
.set(Fields.OWNER_ID, locationId)
.set(Fields.OWNER_ID, minion.ownerId())
.set(Fields.TYPE_ID, minion.type().id())
.set(Fields.LEVEL, minion.level().id())
.set(Fields.CHARGE, minion.charge())
.set(Fields.FACING, minion.facing().ordinal())
.set(Fields.TOOL, minion.tool() == null || minion.tool().getType().isAir() ? null : WrappedItemStack.wrap(minion.tool()).serialize())
.set(Fields.EXTRA_DATA, MinionData.serialize(minion.extraData()))
.execute();
}, databaseExecutor).exceptionallyAsync(throwable -> {
log.error("An unexpected error occurred while inserting minion!", throwable);

View File

@ -7,7 +7,7 @@ import org.jooq.impl.DSL;
public final class Tables {
public static final Table<Record> USERS = DSL.table("axminions_users");
public static final Table<Record> TYPES = DSL.table("axminions_types");
public static final Table<Record> WORLDS = DSL.table("axminions_locations");
public static final Table<Record> WORLDS = DSL.table("axminions_worlds");
public static final Table<Record> LOCATIONS = DSL.table("axminions_locations");
public static final Table<Record> MINIONS = DSL.table("axminions_minions");
}

View File

@ -1,13 +1,91 @@
package com.artillexstudios.axminions.listeners;
import com.artillexstudios.axapi.items.WrappedItemStack;
import com.artillexstudios.axapi.items.component.DataComponents;
import com.artillexstudios.axapi.items.nbt.CompoundTag;
import com.artillexstudios.axminions.config.Minions;
import com.artillexstudios.axminions.database.DataHandler;
import com.artillexstudios.axminions.minions.Level;
import com.artillexstudios.axminions.minions.Minion;
import com.artillexstudios.axminions.minions.MinionArea;
import com.artillexstudios.axminions.minions.MinionData;
import com.artillexstudios.axminions.minions.MinionType;
import com.artillexstudios.axminions.minions.MinionTypes;
import com.artillexstudios.axminions.minions.MinionWorldCache;
import com.artillexstudios.axminions.utils.Direction;
import com.artillexstudios.axminions.utils.LogUtils;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import java.util.HashMap;
public final class MinionPlaceListener implements Listener {
@EventHandler
public void onPlayerInteractEvent(PlayerInteractEvent event) {
LogUtils.debug("MinionPlaceListener - PlayerInteractEvent");
if (event.getAction() != Action.RIGHT_CLICK_BLOCK) {
LogUtils.debug("Not right click block, but {}.", event.getAction());
return;
}
Block clickedBlock = event.getClickedBlock();
if (clickedBlock == null) {
LogUtils.debug("Clicked block is null.");
return;
}
EquipmentSlot hand = event.getHand();
if (hand == null) {
LogUtils.debug("Hand is null!");
return;
}
ItemStack itemStack = event.getPlayer().getInventory().getItem(hand);
if (itemStack == null || itemStack.getType().isAir()) {
LogUtils.debug("Item is either null, or air!");
return;
}
WrappedItemStack wrappedItemStack = WrappedItemStack.wrap(itemStack);
CompoundTag compoundTag = wrappedItemStack.get(DataComponents.customData());
if (!compoundTag.contains("axminions_minion_type")) {
LogUtils.debug("Does not contain miniontype");
return;
}
String typeName = compoundTag.getString("axminions_minion_type");
if (typeName == null || typeName.isBlank()) {
LogUtils.warn("ItemStack in {}'s hand is an invalid minion; the miniontype is empty!", event.getPlayer().getName());
return;
}
MinionType minionType = MinionTypes.parse(typeName);
if (minionType == null) {
LogUtils.warn("ItemStack in {}'s hand is an invalid minion; no miniontype with id {} is loaded!", typeName);
return;
}
// TODO: level
itemStack.setAmount(itemStack.getAmount() - 1);
Location location = clickedBlock.getRelative(event.getBlockFace()).getLocation();
// TODO: Database queries, etc..
// TODO: ownerId
MinionData data = new MinionData(0, minionType, Direction.NORTH, null, minionType.level(1), 0, null, null, new HashMap<>());
Minion minion = new Minion(location, data);
MinionArea area = MinionWorldCache.getArea(location.getWorld());
MinionWorldCache.add(minion);
DataHandler.insertMinion(minion).thenRun(() -> {
LogUtils.debug("Inserted minion!");
});
minion.spawn();
area.startTicking(location.getChunk());
}
}

View File

@ -9,6 +9,7 @@ import com.artillexstudios.axapi.utils.EquipmentSlot;
import com.artillexstudios.axminions.config.Config;
import com.artillexstudios.axminions.integrations.Integrations;
import com.artillexstudios.axminions.minions.skins.Skin;
import com.artillexstudios.axminions.utils.Direction;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.inventory.ItemStack;
@ -83,6 +84,22 @@ public final class Minion {
}
}
public Map<String, String> extraData() {
return this.minionData.extraData();
}
public Direction facing() {
return this.minionData.direction();
}
public long charge() {
return this.minionData.charge();
}
public int ownerId() {
return this.minionData.ownerId();
}
public ItemStack tool() {
return this.minionData.tool();
}

View File

@ -2,6 +2,7 @@ package com.artillexstudios.axminions.minions;
import com.artillexstudios.axminions.minions.skins.Skin;
import com.artillexstudios.axminions.utils.CollectionUtils;
import com.artillexstudios.axminions.utils.Direction;
import org.bukkit.Location;
import org.bukkit.inventory.ItemStack;
@ -10,15 +11,16 @@ import java.util.Map;
import java.util.regex.Pattern;
// The data needed to spawn a minion
public record MinionData(int ownerId, MinionType type, Location linkedChest, Level level, ItemStack tool, Skin skin, HashMap<String, String> storage) {
public record MinionData(int ownerId, MinionType type, Direction direction, Location linkedChest, Level level,
long charge, ItemStack tool, Skin skin, HashMap<String, String> extraData) {
private static final Pattern SEMICOLON = Pattern.compile(";");
private static final Pattern DASH = Pattern.compile("-");
public MinionData withSkin(Skin skin) {
return new MinionData(this.ownerId, this.type, this.linkedChest, this.level, this.tool, skin, this.storage);
public static String serialize(Map<String, String> map) {
if (map.isEmpty()) {
return "";
}
public static String serialize(Map<String, String> map) {
StringBuilder builder = new StringBuilder();
for (Map.Entry<String, String> entry : map.entrySet()) {
builder.append(entry.getKey()).append('-').append(entry.getValue()).append(';');
@ -28,9 +30,9 @@ public record MinionData(int ownerId, MinionType type, Location linkedChest, Lev
return builder.toString();
}
public static Map<String, String> deserialize(String string) {
if (string.isBlank() || !string.contains("-")) {
return Map.of();
public static HashMap<String, String> deserialize(String string) {
if (string.isEmpty()) {
return new HashMap<>(0);
}
String[] split = SEMICOLON.split(string);
@ -43,4 +45,8 @@ public record MinionData(int ownerId, MinionType type, Location linkedChest, Lev
return map;
}
public MinionData withSkin(Skin skin) {
return new MinionData(this.ownerId, this.type, this.direction, this.linkedChest, this.level, this.charge, this.tool, skin, this.extraData);
}
}

View File

@ -93,6 +93,8 @@ public final class MinionType {
CompoundTag tag = wrappedItemStack.get(DataComponents.customData());
// TODO: Store statistics, level, etc
tag.putString("axminions_minion_type", this.name);
wrappedItemStack.set(DataComponents.customData(), tag);
wrappedItemStack.finishEdit();
return wrappedItemStack.toBukkit();
}

View File

@ -0,0 +1,18 @@
package com.artillexstudios.axminions.utils;
import org.bukkit.block.BlockFace;
public enum Direction {
NORTH(180f, BlockFace.NORTH),
WEST(90f, BlockFace.WEST),
SOUTH(0f, BlockFace.SOUTH),
EAST(-90f, BlockFace.EAST);
private final float yaw;
private final BlockFace face;
Direction(float yaw, BlockFace face) {
this.yaw = yaw;
this.face = face;
}
}