Tickers for folia and bukkit

This commit is contained in:
TomTom 2024-07-19 18:39:12 +02:00
parent 05fb844b76
commit 39cf3051ea
10 changed files with 97 additions and 38 deletions

View File

@ -41,11 +41,11 @@ public final class AxMinionsCommand {
.withPermission("axminions.command.debug.spawn")
.executesPlayer((sender, args) -> {
Location location = LocationUtils.toBlockCenter(sender.getLocation());
MinionData data = new MinionData(0, MinionTypes.parse("miner"), new Level(1, 20, new Skin("cool", Map.ofEntries(Map.entry(EquipmentSlot.BOOTS, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_BOOTS))), Map.entry(EquipmentSlot.LEGGINGS, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_LEGGINGS))), Map.entry(EquipmentSlot.CHEST_PLATE, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_CHESTPLATE))), Map.entry(EquipmentSlot.HELMET, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_HELMET))), Map.entry(EquipmentSlot.MAIN_HAND, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_PICKAXE))), Map.entry(EquipmentSlot.OFF_HAND, WrappedItemStack.wrap(new ItemStack(Material.AIR)))))), new ItemStack(Material.DIAMOND_HOE), new Skin("cool", Map.ofEntries(Map.entry(EquipmentSlot.BOOTS, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_BOOTS))), Map.entry(EquipmentSlot.LEGGINGS, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_LEGGINGS))), Map.entry(EquipmentSlot.CHEST_PLATE, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_CHESTPLATE))), Map.entry(EquipmentSlot.HELMET, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_HELMET))), Map.entry(EquipmentSlot.MAIN_HAND, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_PICKAXE))), Map.entry(EquipmentSlot.OFF_HAND, WrappedItemStack.wrap(new ItemStack(Material.AIR))))), new HashMap<>());
MinionData data = new MinionData(0, MinionTypes.parse("miner"), new Level(1, 80, new Skin("cool", Map.ofEntries(Map.entry(EquipmentSlot.BOOTS, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_BOOTS))), Map.entry(EquipmentSlot.LEGGINGS, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_LEGGINGS))), Map.entry(EquipmentSlot.CHEST_PLATE, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_CHESTPLATE))), Map.entry(EquipmentSlot.HELMET, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_HELMET))), Map.entry(EquipmentSlot.MAIN_HAND, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_PICKAXE))), Map.entry(EquipmentSlot.OFF_HAND, WrappedItemStack.wrap(new ItemStack(Material.AIR)))))), new ItemStack(Material.DIAMOND_HOE), new Skin("cool", Map.ofEntries(Map.entry(EquipmentSlot.BOOTS, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_BOOTS))), Map.entry(EquipmentSlot.LEGGINGS, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_LEGGINGS))), Map.entry(EquipmentSlot.CHEST_PLATE, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_CHESTPLATE))), Map.entry(EquipmentSlot.HELMET, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_HELMET))), Map.entry(EquipmentSlot.MAIN_HAND, WrappedItemStack.wrap(new ItemStack(Material.DIAMOND_PICKAXE))), Map.entry(EquipmentSlot.OFF_HAND, WrappedItemStack.wrap(new ItemStack(Material.AIR))))), new HashMap<>());
Minion minion = new Minion(location, data);
minion.spawn();
MinionArea area = MinionWorldCache.getArea(location.getWorld());
area.load(minion);
MinionWorldCache.add(minion);
area.startTicking(location.getChunk());
})
)

View File

@ -1,6 +1,7 @@
package com.artillexstudios.axminions.exception;
public class MinionTickFailException extends IllegalStateException {
// We don't always care about the stacktrace
public static final MinionTickFailException INSTANCE = new MinionTickFailException();
public MinionTickFailException() {

View File

@ -22,6 +22,7 @@ public final class Minion {
private final AtomicBoolean needsSaving = new AtomicBoolean(false);
private int tick = 0;
private int armTick = 0;
private boolean ticking = false;
private MinionData minionData;
public Minion(Location location, MinionData data) {
@ -40,7 +41,7 @@ public final class Minion {
if (Config.SHOW_HAND_ANIMATION) {
if (this.armTick >= 20) return;
ArmorStandMeta meta = (ArmorStandMeta) this.entity.meta();
meta.metadata().set(Accessors.RIGHT_ARM_ROTATION, new EulerAngle((double) (-2 + this.armTick) / 10, 0, 0));
meta.metadata().set(Accessors.RIGHT_ARM_ROTATION, new EulerAngle((-2 + ((double) this.armTick / 10)), 0, 0));
this.armTick += 2;
}
return;
@ -48,7 +49,6 @@ public final class Minion {
this.tick = 0;
if (Config.SHOW_HAND_ANIMATION & this.minionData.type().tick(this)) {
// TODO: Animation
this.armTick = 0;
}
}
@ -74,6 +74,10 @@ public final class Minion {
return this.minionData.skin();
}
public MinionType type() {
return this.minionData.type();
}
public void spawn() {
this.entity.spawn();
}
@ -90,6 +94,14 @@ public final class Minion {
return this.location;
}
public boolean ticking() {
return this.ticking;
}
public void ticking(boolean ticking) {
this.ticking = ticking;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -1,6 +1,7 @@
package com.artillexstudios.axminions.minions;
import com.artillexstudios.axminions.utils.LogUtils;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import org.bukkit.World;
import java.util.Collection;
@ -8,6 +9,7 @@ import java.util.IdentityHashMap;
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) {
if (worlds.containsKey(world)) {
@ -18,6 +20,18 @@ public final class MinionWorldCache {
worlds.put(world, new MinionArea());
}
public static void add(Minion minion) {
minions.add(minion);
MinionArea area = worlds.get(minion.location().getWorld());
area.load(minion);
}
public static void remove(Minion minion) {
minions.remove(minion);
MinionArea area = worlds.get(minion.location().getWorld());
area.remove(minion);
}
public static MinionArea getArea(World world) {
return worlds.get(world);
}
@ -26,6 +40,10 @@ public final class MinionWorldCache {
return worlds.remove(world);
}
public static ObjectArrayList<Minion> minions() {
return minions;
}
public static Collection<MinionArea> worlds() {
return worlds.values();
}

View File

@ -98,12 +98,12 @@ public final class CompiledAction {
try {
effect.dispatch(minion, collected);
} catch (MinionTickFailException exception) {
LogUtils.warn("An unexpected error occurred while ticking minion {} at {}!", minion, minion.location(), exception);
LogUtils.warn("An unexpected error occurred while ticking minion {} at {}!", minion.type().name(), minion.location(), exception);
throw exception;
}
}
});
// TODO: Return false if the actions can't be ran
return true;
}
}

View File

@ -6,7 +6,6 @@ import com.artillexstudios.axminions.minions.actions.effects.Effect;
import com.artillexstudios.axminions.utils.ItemCollection;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Registry;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.block.data.BlockData;
@ -29,7 +28,7 @@ public class BreakEffect extends Effect<Location, ItemCollection> {
World world = argument.getWorld();
if (world == null) {
throw MinionTickFailException.INSTANCE;
throw new MinionTickFailException("World is null!");
}
world.setBlockData(argument, AIR);

View File

@ -1,5 +1,6 @@
package com.artillexstudios.axminions.minions.actions.effects.implementation;
import com.artillexstudios.axminions.exception.MinionTickFailException;
import com.artillexstudios.axminions.minions.Minion;
import com.artillexstudios.axminions.minions.actions.effects.Effect;
import com.artillexstudios.axminions.utils.ItemCollection;
@ -18,7 +19,7 @@ public class DropAtMinionEffect extends Effect<ItemCollection, ItemCollection> {
public ItemCollection run(Minion minion, ItemCollection argument) {
World world = minion.location().getWorld();
if (world == null) {
return ItemCollection.EMPTY;
throw new MinionTickFailException("World is null!");
}
for (ItemStack itemStack : argument.items()) {

View File

@ -4,53 +4,38 @@ import com.artillexstudios.axapi.scheduler.ScheduledTask;
import com.artillexstudios.axapi.scheduler.Scheduler;
import com.artillexstudios.axminions.jfr.MinionTickEvent;
import com.artillexstudios.axminions.minions.Minion;
import com.artillexstudios.axminions.minions.MinionArea;
import com.artillexstudios.axminions.minions.MinionTicker;
import com.artillexstudios.axminions.minions.MinionWorldCache;
import com.artillexstudios.axminions.utils.ChunkPos;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
// TODO: Add a list with all minions, add a ticking boolean to minions
public final class BukkitMinionTicker implements MinionTicker {
private static ScheduledTask task;
private ScheduledTask task;
@Override
public void start() {
task = Scheduler.get().runTimer(() -> {
this.task = Scheduler.get().runTimer(() -> {
MinionTickEvent event = new MinionTickEvent();
event.begin();
for (MinionArea world : MinionWorldCache.worlds()) {
world.forEachPos(position -> {
if (!position.isTicking()) return;
tickChunk(position);
});
final ObjectArrayList<Minion> minions = MinionWorldCache.minions();
final int minionSize = minions.size();
for (int i = 0; i < minionSize; i++) {
final Minion minion = minions.get(i);
if (!minion.ticking()) {
continue;
}
minion.tick();
}
event.commit();
}, 1, 1);
}
private static void tickChunk(ChunkPos position) {
ObjectArrayList<Minion> minions = position.minions();
int minionSize = minions.size();
if (minionSize == 0) return;
Minion first = minions.get(0);
Scheduler.get().executeAt(first.location(), () -> {
// We already have the first, why not tick it?
first.tick();
for (int i = 1; i < minionSize; i++) {
Minion minion = minions.get(i);
minion.tick();
}
});
}
@Override
public void cancel() {
if (task != null && !task.isCancelled()) {
task.cancel();
if (this.task != null && !this.task.isCancelled()) {
this.task.cancel();
}
}
}

View File

@ -1,16 +1,55 @@
package com.artillexstudios.axminions.minions.ticker;
import com.artillexstudios.axapi.scheduler.ScheduledTask;
import com.artillexstudios.axapi.scheduler.Scheduler;
import com.artillexstudios.axminions.jfr.MinionTickEvent;
import com.artillexstudios.axminions.minions.Minion;
import com.artillexstudios.axminions.minions.MinionArea;
import com.artillexstudios.axminions.minions.MinionTicker;
import com.artillexstudios.axminions.minions.MinionWorldCache;
import com.artillexstudios.axminions.utils.ChunkPos;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
public final class FoliaMinionTicker implements MinionTicker {
private ScheduledTask task;
@Override
public void start() {
this.task = Scheduler.get().runTimer(() -> {
MinionTickEvent event = new MinionTickEvent();
event.begin();
for (MinionArea world : MinionWorldCache.worlds()) {
world.forEachPos(position -> {
if (!position.isTicking()) return;
tickChunk(position);
});
}
event.commit();
}, 1, 1);
}
private void tickChunk(ChunkPos position) {
ObjectArrayList<Minion> minions = position.minions();
int minionSize = minions.size();
if (minionSize == 0) return;
Minion first = minions.get(0);
Scheduler.get().executeAt(first.location(), () -> {
// We already have the first, why not tick it?
first.tick();
for (int i = 1; i < minionSize; i++) {
Minion minion = minions.get(i);
minion.tick();
}
});
}
@Override
public void cancel() {
if (this.task != null && !this.task.isCancelled()) {
this.task.cancel();
}
}
}

View File

@ -9,6 +9,10 @@ public record ChunkPos(int x, int z, AtomicBoolean ticking, ObjectArrayList<Mini
public void ticking(boolean ticking) {
this.ticking.set(ticking);
for (Minion minion : this.minions) {
minion.ticking(ticking);
}
}
public boolean isTicking() {