Performance, fixes

This commit is contained in:
TomTom 2024-08-20 16:57:13 +02:00
parent c8a6d01b0b
commit 2539ab790e
14 changed files with 127 additions and 27 deletions

View File

@ -42,7 +42,6 @@ public final class Skins {
Skin skin = Skin.of(route, mapList);
SkinRegistry.register(skin);
}
// TODO: Refresh minion skins
return true;
}

View File

@ -136,12 +136,29 @@ public final class DataHandler {
.select()
.from(Tables.USERS)
.where(Fields.UUID.eq(player.getUniqueId()))
.limit(1)
.fetch();
if (!select.isEmpty()) {
Record record = select.get(0);
LogUtils.debug("User data select record: {}", record);
return new User(record.get(Fields.ID), player.getUniqueId(), player.getName(), texture, record.get(Fields.EXTRA_SLOTS, int.class), record.get(Fields.ISLAND_SLOTS, int.class));
int ownerId = record.get(Fields.ID);
Result<Record1<Integer>> minionSelect = DatabaseConnector.getInstance().context()
.selectCount()
.from(Tables.MINIONS)
.where(Fields.OWNER_ID.eq(ownerId))
.limit(1)
.fetch();
if (!minionSelect.isEmpty()) {
Record minionRecord = minionSelect.get(0);
int minionCount = minionRecord.get(0, int.class);
return new User(ownerId, player.getUniqueId(), player.getName(), texture, minionCount, record.get(Fields.EXTRA_SLOTS, int.class), record.get(Fields.ISLAND_SLOTS, int.class), new ArrayList<>());
}
return new User(ownerId, player.getUniqueId(), player.getName(), texture, 0, record.get(Fields.EXTRA_SLOTS, int.class), record.get(Fields.ISLAND_SLOTS, int.class), new ArrayList<>());
}
Record1<Integer> insert = DatabaseConnector.getInstance().context()
@ -157,12 +174,11 @@ public final class DataHandler {
return null;
}
return new User(insert.get(Fields.ID), player.getUniqueId(), player.getName(), texture, 0, 0);
return new User(insert.get(Fields.ID), player.getUniqueId(), player.getName(), texture, 0, 0, 0, new ArrayList<>());
}, AsyncUtils.executor()).exceptionallyAsync(throwable -> {
log.error("An unexpected error occurred while updating user {}!", player.getName(), throwable);
return null;
}, AsyncUtils.executor());
}
public static int worldId(World world) {
@ -170,6 +186,7 @@ public final class DataHandler {
.select()
.from(Tables.WORLDS)
.where(Fields.WORLD_UUID.eq(world.getUID()))
.limit(1)
.fetch();
if (!select.isEmpty()) {
@ -199,6 +216,7 @@ public final class DataHandler {
.and(Fields.LOCATION_X.eq(location.getBlockX()))
.and(Fields.LOCATION_Y.eq(location.getBlockY()))
.and(Fields.LOCATION_Z.eq(location.getBlockZ())))
.limit(1)
.fetch();
if (!select.isEmpty()) {
@ -228,6 +246,7 @@ public final class DataHandler {
.select()
.from(Tables.TYPES)
.where(Fields.ID.eq((int) id))
.limit(1)
.fetchSingle(Fields.NAME);
}
@ -277,6 +296,7 @@ public final class DataHandler {
return IntLongPair.of(0, 0);
}
List<Minion> toLoad = new ArrayList<>();
Result<Record> locations = DatabaseConnector.getInstance().context()
.select()
.from(Tables.LOCATIONS)
@ -289,6 +309,7 @@ public final class DataHandler {
.select()
.from(Tables.MINIONS)
.where(Fields.LOCATION_ID.eq(id))
.limit(1)
.fetch();
if (minions.isEmpty()) {
@ -321,10 +342,12 @@ public final class DataHandler {
MinionData data = new MinionData(ownerId, type, Direction.entries[facing], null, minionLevel, charge, tool == null ? new ItemStack(Material.AIR) : WrappedItemStack.wrap(tool).toBukkit(), null, MinionData.deserialize(extraData));
Minion minion = new Minion(new Location(world, x + 0.5, y, z + 0.5), data);
MinionWorldCache.add(minion);
toLoad.add(minion);
minion.spawn();
loadedMinions++;
}
MinionWorldCache.addAll(toLoad);
long took = System.nanoTime() - start;
return IntLongPair.of(loadedMinions, took);
}, AsyncUtils.executor()).exceptionallyAsync(throwable -> {
@ -340,6 +363,7 @@ public final class DataHandler {
.select()
.from(Tables.TYPES)
.where(Fields.NAME.eq(type.name()))
.limit(1)
.fetch();
if (!select.isEmpty()) {
@ -395,7 +419,7 @@ public final class DataHandler {
.set(Fields.LEVEL, minion.level())
.set(Fields.CHARGE, minion.charge())
.set(Fields.FACING, minion.facing().ordinal())
.set(Fields.TOOL, WrappedItemStack.wrap(minion.tool()).serialize())
.set(Fields.TOOL, minion.tool().getType().isAir() ? new byte[0] : WrappedItemStack.wrap(minion.tool()).serialize())
.set(Fields.EXTRA_DATA, MinionData.serialize(minion.extraData()))
.where(Fields.LOCATION_ID.eq(locationId));

View File

@ -82,7 +82,6 @@ public final class MinionPlaceListener implements Listener {
}
// TODO: level
itemStack.setAmount(itemStack.getAmount() - 1);
Location location = LocationUtils.toBlockCenter(clickedBlock.getRelative(event.getBlockFace()).getLocation());
MinionArea area = MinionWorldCache.getArea(location.getWorld());
@ -97,15 +96,19 @@ public final class MinionPlaceListener implements Listener {
return;
}
itemStack.setAmount(itemStack.getAmount() - 1);
LogUtils.debug("Minion count for user: " + user.minionCount());
// TODO: Database queries, etc..
MinionData data = new MinionData(user.id(), minionType, Direction.NORTH, null, minionType.level(1), 0, null, null, new HashMap<>());
data.extraData().put("owner_texture", NMSHandlers.getNmsHandler().textures(event.getPlayer()).getKey());
Minion minion = new Minion(location, data);
MinionWorldCache.add(minion);
user.minionCount(user.minionCount() + 1);
DataHandler.insertMinion(minion).thenRun(() -> {
LogUtils.debug("Inserted minion!");
minion.spawn();
area.startTicking(location.getChunk());
});
minion.spawn();
area.startTicking(location.getChunk());
}
}

View File

@ -1,10 +1,11 @@
package com.artillexstudios.axminions.listeners;
import com.artillexstudios.axminions.database.DataHandler;
import com.artillexstudios.axminions.users.User;
import com.artillexstudios.axminions.minions.Minion;
import com.artillexstudios.axminions.minions.MinionWorldCache;
import com.artillexstudios.axminions.users.Users;
import com.artillexstudios.axminions.utils.LogUtils;
import org.bukkit.entity.Player;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
@ -14,9 +15,22 @@ public final class PlayerListener implements Listener {
@EventHandler
public void onPlayerJoinEvent(PlayerJoinEvent event) {
DataHandler.loadUser(event.getPlayer()).thenAccept(user -> {
LogUtils.debug("Loaded user");
if (user == null) {
LogUtils.warn("Failed to load user data for player {}!", event.getPlayer().getName());
return;
}
LogUtils.debug("Loaded user for player: {}", event.getPlayer().getName());
Users.load(user);
// TODO: Update all minions placed by the user
ObjectArrayList<Minion> copy = MinionWorldCache.copy();
for (Minion minion : copy) {
if (minion.ownerId() == user.id()) {
user.minions().add(minion);
minion.extraData().put("owner_texture", user.texture());
minion.skin(minion.skin());
}
}
});
}
}

View File

@ -1,6 +1,12 @@
package com.artillexstudios.axminions.listeners;
import com.artillexstudios.axapi.scheduler.Scheduler;
import com.artillexstudios.axminions.database.DataHandler;
import com.artillexstudios.axminions.minions.MinionArea;
import com.artillexstudios.axminions.minions.MinionWorldCache;
import com.artillexstudios.axminions.utils.LogUtils;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.world.WorldLoadEvent;
@ -10,7 +16,24 @@ public final class WorldListener implements Listener {
@EventHandler
public void onWorldLoadEvent(WorldLoadEvent event) {
MinionWorldCache.loadArea(event.getWorld());
MinionArea area = MinionWorldCache.loadArea(event.getWorld());
DataHandler.loadMinions(event.getWorld()).toCompletableFuture().thenAccept(loaded -> {
LogUtils.debug("Loaded {} minions in world {} in {} ms!", loaded.firstInt(), event.getWorld().getName(), loaded.secondLong() / 1_000_000);
if (area == null) {
return;
}
Scheduler.get().run(() -> {
if (Bukkit.getWorld(event.getWorld().getUID()) == null) {
return;
}
for (Chunk chunk : event.getWorld().getLoadedChunks()) {
area.startTicking(chunk);
}
});
});
}
@EventHandler

View File

@ -149,6 +149,7 @@ public final class Minion {
}
public void destroy() {
this.ticking = false;
this.entity.remove();
}

View File

@ -36,6 +36,11 @@ public final class MinionSaver {
if (this.future != null && !this.future.isCancelled()) {
this.future.cancel(false);
this.future = null;
ObjectArrayList<Minion> copy = MinionWorldCache.copy();
DataHandler.saveMinions(copy).toCompletableFuture().thenAccept(pair -> {
LogUtils.debug("Saved {} minions in {} ms!", pair.firstLong(), pair.secondLong() / 1_000_000L);
}).join();
}
}
}

View File

@ -6,18 +6,21 @@ import org.bukkit.World;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.List;
public final class MinionWorldCache {
private static final IdentityHashMap<World, MinionArea> worlds = new IdentityHashMap<>();
private static final ObjectArrayList<Minion> minions = new ObjectArrayList<>();
public static void loadArea(World world) {
public static MinionArea loadArea(World world) {
if (worlds.containsKey(world)) {
LogUtils.warn("An area is already present for world {}", world.getName());
return;
return null;
}
worlds.put(world, new MinionArea());
MinionArea area = new MinionArea();
worlds.put(world, area);
return area;
}
public static void add(Minion minion) {
@ -31,6 +34,17 @@ public final class MinionWorldCache {
area.load(minion);
}
public static void addAll(List<Minion> list) {
minions.addAll(list);
MinionArea area = worlds.get(list.get(0).location().getWorld());
if (area == null) {
LogUtils.error("Tried to add minions to unknown world! {}", list);
return;
}
area.loadAll(list);
}
public static void remove(Minion minion) {
minions.remove(minion);
MinionArea area = worlds.get(minion.location().getWorld());
@ -60,6 +74,7 @@ public final class MinionWorldCache {
area.forEachPos(position -> {
for (Minion minion : position.minions()) {
minions.remove(minion);
minion.destroy();
}
});

View File

@ -4,6 +4,7 @@ import com.artillexstudios.axminions.exception.MinionTickFailException;
import com.artillexstudios.axminions.minions.actions.filters.Filter;
import com.artillexstudios.axminions.utils.LogUtils;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity;
@ -18,6 +19,7 @@ public enum CollectorShape {
final int blockX = location.getBlockX();
final int blockY = location.getBlockY();
final int blockZ = location.getBlockZ();
final World world = location.getWorld();
final double rangeSquared = range * range;
final double smallRangeSquared = ((range - 1) * (range - 1));
@ -39,7 +41,7 @@ public enum CollectorShape {
if (distance < rangeSquared && distance < smallRangeSquared) {
try {
Location newLocation = new Location(location.getWorld(), x, y, z);
Location newLocation = new Location(world, x, y, z);
for (Filter<?> filter : filters) {
if (!filter.isAllowed(newLocation)) {
continue z;
@ -70,6 +72,7 @@ public enum CollectorShape {
final int blockX = location.getBlockX();
final int blockY = location.getBlockY();
final int blockZ = location.getBlockZ();
final World world = location.getWorld();
final double rangeSquared = range * range;
final double smallRangeSquared = ((range - 1) * (range - 1));
@ -87,7 +90,7 @@ public enum CollectorShape {
if (distance < rangeSquared && distance < smallRangeSquared) {
try {
Location newLocation = new Location(location.getWorld(), x, blockY, z);
Location newLocation = new Location(world, x, blockY, z);
for (Filter<?> filter : filters) {
if (!filter.isAllowed(newLocation)) {
continue z;
@ -117,6 +120,7 @@ public enum CollectorShape {
final int blockX = location.getBlockX();
final int blockY = location.getBlockY();
final int blockZ = location.getBlockZ();
final World world = location.getWorld();
final int xStart = (int) Math.round(blockX - range);
final int xEnd = (int) Math.round(blockX + range);
@ -127,7 +131,7 @@ public enum CollectorShape {
z:
for (int z = zStart; z <= zEnd; z++) {
try {
Location newLocation = new Location(location.getWorld(), x, blockY, z);
Location newLocation = new Location(world, x, blockY, z);
for (Filter<?> filter : filters) {
if (!filter.isAllowed(newLocation)) {
continue z;
@ -156,6 +160,7 @@ public enum CollectorShape {
final int blockX = location.getBlockX();
final int blockY = location.getBlockY();
final int blockZ = location.getBlockZ();
final World world = location.getWorld();
final int xStart = (int) Math.round(blockX - range);
final int xEnd = (int) Math.round(blockX + range);
@ -169,7 +174,7 @@ public enum CollectorShape {
z:
for (int z = zStart; z <= zEnd; z++) {
try {
Location newLocation = new Location(location.getWorld(), x, y, z);
Location newLocation = new Location(world, x, y, z);
for (Filter<?> filter : filters) {
if (!filter.isAllowed(newLocation)) {
continue z;

View File

@ -1,14 +1,14 @@
package com.artillexstudios.axminions.minions.actions.filters;
import com.artillexstudios.axminions.exception.TransformerNotPresentException;
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
public abstract class Filter<T> {
private final IdentityHashMap<Class<?>, Transformer<?, ?>> transformers = new IdentityHashMap<>();
private final Object2ObjectArrayMap<Class<?>, Transformer<?, ?>> transformers = new Object2ObjectArrayMap<>();
private final Collection<Transformer<?, ?>> unmodifiableTransformers = Collections.unmodifiableCollection(transformers.values());
public abstract boolean isAllowed(Object object);

View File

@ -9,14 +9,16 @@ import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import java.util.HashSet;
import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class MaterialFilter extends Filter<Material> {
private final HashSet<Material> allowed = new HashSet<>();
private final Set<Material> allowed = Collections.newSetFromMap(new EnumMap<>(Material.class));
public MaterialFilter(Map<Object, Object> configuration) {
this.addTransformer(Location.class, new Transformer<Location, Material>() {

View File

@ -1,6 +1,13 @@
package com.artillexstudios.axminions.users;
import com.artillexstudios.axminions.minions.Minion;
import java.util.List;
import java.util.UUID;
public record User(int id, UUID uuid, String name, String texture, int extraSlots, int extraIslandSlots) {
public record User(int id, UUID uuid, String name, String texture, int minionCount, int extraSlots, int extraIslandSlots, List<Minion> minions) {
public void minionCount(int minionCount) {
Users.load(new User(this.id, this.uuid, this.name, this.texture, minionCount, this.extraSlots, this.extraIslandSlots, this.minions));
}
}

View File

@ -5,7 +5,7 @@ import org.bukkit.entity.Player;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class Users {
public final class Users {
private static final ConcurrentHashMap<UUID, User> USER_CACHE = new ConcurrentHashMap<>(100);
public static void load(User user) {

View File

@ -20,6 +20,8 @@ public record ChunkPos(int x, int z, AtomicBoolean ticking, ObjectArrayList<Mini
}
public void addMinion(Minion minion) {
minion.ticking(this.ticking.get());
this.minions.add(minion);
}