diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java index e68d9c091..4af4ae27c 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/Movecraft.java @@ -26,6 +26,8 @@ import net.countercraft.movecraft.features.contacts.ContactsCommand; import net.countercraft.movecraft.features.contacts.ContactsManager; import net.countercraft.movecraft.features.contacts.ContactsSign; +import net.countercraft.movecraft.features.status.StatusManager; +import net.countercraft.movecraft.features.status.StatusSign; import net.countercraft.movecraft.listener.BlockListener; import net.countercraft.movecraft.listener.InteractListener; import net.countercraft.movecraft.listener.PlayerListener; @@ -217,7 +219,6 @@ public void onEnable() { getServer().getPluginManager().registerEvents(new ReleaseSign(), this); getServer().getPluginManager().registerEvents(new RemoteSign(), this); getServer().getPluginManager().registerEvents(new SpeedSign(), this); - getServer().getPluginManager().registerEvents(new StatusSign(), this); getServer().getPluginManager().registerEvents(new SubcraftRotateSign(), this); getServer().getPluginManager().registerEvents(new TeleportSign(), this); getServer().getPluginManager().registerEvents(new ScuttleSign(), this); @@ -225,9 +226,14 @@ public void onEnable() { var contactsManager = new ContactsManager(); contactsManager.runTaskTimerAsynchronously(this, 0, 20); getServer().getPluginManager().registerEvents(contactsManager, this); - getServer().getPluginManager().registerEvents(new ContactsSign(contactsManager), this); + getServer().getPluginManager().registerEvents(new ContactsSign(), this); getCommand("contacts").setExecutor(new ContactsCommand()); + var statusManager = new StatusManager(); + statusManager.runTaskTimerAsynchronously(this, 0, 1); + getServer().getPluginManager().registerEvents(statusManager, this); + getServer().getPluginManager().registerEvents(new StatusSign(), this); + logger.info("[V " + getDescription().getVersion() + "] has been enabled."); } diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/async/AsyncManager.java b/Movecraft/src/main/java/net/countercraft/movecraft/async/AsyncManager.java index c71dec0f9..fc2cfe889 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/async/AsyncManager.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/async/AsyncManager.java @@ -24,44 +24,23 @@ import net.countercraft.movecraft.async.rotation.RotationTask; import net.countercraft.movecraft.async.translation.TranslationTask; import net.countercraft.movecraft.config.Settings; -import net.countercraft.movecraft.craft.Craft; -import net.countercraft.movecraft.craft.CraftManager; -import net.countercraft.movecraft.craft.CraftStatus; -import net.countercraft.movecraft.craft.PilotedCraft; -import net.countercraft.movecraft.craft.PlayerCraft; -import net.countercraft.movecraft.craft.SinkingCraft; +import net.countercraft.movecraft.craft.*; import net.countercraft.movecraft.craft.type.CraftType; -import net.countercraft.movecraft.craft.type.RequiredBlockEntry; import net.countercraft.movecraft.events.CraftReleaseEvent; -import net.countercraft.movecraft.exception.EmptyHitBoxException; -import net.countercraft.movecraft.localisation.I18nSupport; import net.countercraft.movecraft.mapUpdater.MapUpdateManager; import net.countercraft.movecraft.mapUpdater.update.BlockCreateCommand; import net.countercraft.movecraft.mapUpdater.update.UpdateCommand; -import net.countercraft.movecraft.util.Counter; -import net.countercraft.movecraft.util.Tags; import net.countercraft.movecraft.util.hitboxes.HitBox; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.sound.Sound; import net.kyori.adventure.text.Component; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; import org.bukkit.block.data.BlockData; import org.bukkit.entity.Player; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; import org.bukkit.scheduler.BukkitRunnable; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.WeakHashMap; +import java.util.*; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; @@ -319,40 +298,6 @@ else if (dive) { } } - private void detectSinking(){ - for(Craft craft : CraftManager.getInstance()) { - if (craft instanceof SinkingCraft) - continue; - if (craft.getType().getDoubleProperty(CraftType.SINK_PERCENT) == 0.0 || !craft.isNotProcessing()) - continue; - long ticksElapsed = (System.currentTimeMillis() - craft.getLastBlockCheck()) / 50; - - if (ticksElapsed <= Settings.SinkCheckTicks) - continue; - - CraftStatus status = checkCraftStatus(craft); - //If the craft is disabled, play a sound and disable it. - //Only do this if the craft isn't already disabled. - if (status.isDisabled() && craft.isNotProcessing() && !craft.getDisabled()) { - craft.setDisabled(true); - craft.getAudience().playSound(Sound.sound(Key.key("entity.iron_golem.death"), Sound.Source.NEUTRAL, 5.0f, 5.0f)); - } - - - // if the craft is sinking, let the player - // know and release the craft. Otherwise - // update the time for the next check - if (status.isSinking() && craft.isNotProcessing()) { - craft.getAudience().sendMessage(I18nSupport.getInternationalisedComponent("Player - Craft is sinking")); - craft.setCruising(false); - CraftManager.getInstance().sink(craft); - } - else { - craft.setLastBlockCheck(System.currentTimeMillis()); - } - } - } - //Controls sinking crafts private void processSinking() { //copy the crafts before iteration to prevent concurrent modifications @@ -440,7 +385,6 @@ public void run() { clearAll(); processCruise(); - detectSinking(); processSinking(); processFadingBlocks(); processAlgorithmQueue(); @@ -472,95 +416,4 @@ private void clearAll() { clearanceSet.clear(); } - - public CraftStatus checkCraftStatus(@NotNull Craft craft) { - boolean isSinking = false; - boolean isDisabled = false; - - // Create counters and populate with required block entries - Counter<RequiredBlockEntry> flyBlocks = new Counter<>(); - flyBlocks.putAll(craft.getType().getRequiredBlockProperty(CraftType.FLY_BLOCKS)); - Counter<RequiredBlockEntry> moveBlocks = new Counter<>(); - moveBlocks.putAll(craft.getType().getRequiredBlockProperty(CraftType.MOVE_BLOCKS)); - - Counter<Material> materials = new Counter<>(); - var v = craft.getType().getObjectProperty(CraftType.FUEL_TYPES); - if(!(v instanceof Map<?, ?>)) - throw new IllegalStateException("FUEL_TYPES must be of type Map"); - var fuelTypes = (Map<?, ?>) v; - for(var e : fuelTypes.entrySet()) { - if(!(e.getKey() instanceof Material)) - throw new IllegalStateException("Keys in FUEL_TYPES must be of type Material"); - if(!(e.getValue() instanceof Double)) - throw new IllegalStateException("Values in FUEL_TYPES must be of type Double"); - } - - // go through each block in the HitBox, and if it's in the FlyBlocks or MoveBlocks, increment the counter - int totalNonNegligibleBlocks = 0; - int totalNonNegligibleWaterBlocks = 0; - double fuel = 0; - for (MovecraftLocation l : craft.getHitBox()) { - Material type = craft.getWorld().getBlockAt(l.getX(), l.getY(), l.getZ()).getType(); - for(RequiredBlockEntry entry : flyBlocks.getKeySet()) { - if(entry.contains(type)) - flyBlocks.add(entry); - } - for(RequiredBlockEntry entry : moveBlocks.getKeySet()) { - if(entry.contains(type)) - moveBlocks.add(entry); - } - materials.add(type); - - if (type != Material.FIRE && !type.isAir()) { - totalNonNegligibleBlocks++; - } - if (type != Material.FIRE && !type.isAir() && !Tags.FLUID.contains(type)) { - totalNonNegligibleWaterBlocks++; - } - - if(Tags.FURNACES.contains(type)) { - InventoryHolder inventoryHolder = (InventoryHolder) craft.getWorld().getBlockAt(l.getX(), l.getY(), l.getZ()).getState(); - for (ItemStack iStack : inventoryHolder.getInventory()) { - if (iStack == null || !fuelTypes.containsKey(iStack.getType())) - continue; - fuel += iStack.getAmount() * (double) fuelTypes.get(iStack.getType()); - } - } - } - - // now see if any of the resulting percentages - // are below the threshold specified in sinkPercent - double sinkPercent = craft.getType().getDoubleProperty(CraftType.SINK_PERCENT) / 100.0; - for(RequiredBlockEntry entry : flyBlocks.getKeySet()) { - if(!entry.check(flyBlocks.get(entry), totalNonNegligibleBlocks, sinkPercent)) - isSinking = true; - } - for(RequiredBlockEntry entry : moveBlocks.getKeySet()) { - if(!entry.check(moveBlocks.get(entry), totalNonNegligibleBlocks, sinkPercent)) - isDisabled = !craft.getDisabled() && craft.isNotProcessing(); - } - - // And check the OverallSinkPercent - if (craft.getType().getDoubleProperty(CraftType.OVERALL_SINK_PERCENT) != 0.0) { - double percent; - if (craft.getType().getBoolProperty(CraftType.BLOCKED_BY_WATER)) { - percent = (double) totalNonNegligibleBlocks - / (double) craft.getOrigBlockCount(); - } - else { - percent = (double) totalNonNegligibleWaterBlocks - / (double) craft.getOrigBlockCount(); - } - if (percent * 100.0 < craft.getType().getDoubleProperty(CraftType.OVERALL_SINK_PERCENT)) - isSinking = true; - } - - if (totalNonNegligibleBlocks == 0) - isSinking = true; - - craft.updateMaterials(materials); - craft.setTotalFuel(fuel); - - return CraftStatus.of(isSinking, isDisabled); - } } diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/craft/BaseCraft.java b/Movecraft/src/main/java/net/countercraft/movecraft/craft/BaseCraft.java index 7622673a2..df8f4b336 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/craft/BaseCraft.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/craft/BaseCraft.java @@ -42,8 +42,6 @@ public abstract class BaseCraft implements Craft { @NotNull protected final MutableHitBox collapsedHitBox; @NotNull - protected Counter<Material> materials; - @NotNull private final AtomicBoolean processing = new AtomicBoolean(); private final long origPilotTime; @NotNull @@ -65,7 +63,6 @@ public abstract class BaseCraft implements Craft { private int currentGear = 1; private double burningFuel; private int origBlockCount; - private double totalFuel = 0; @NotNull private Audience audience; @NotNull @@ -85,7 +82,6 @@ public BaseCraft(@NotNull CraftType type, @NotNull World world) { cruising = false; disabled = false; origPilotTime = System.currentTimeMillis(); - materials = new Counter<>(); audience = Audience.empty(); } @@ -324,17 +320,15 @@ public int getTickCooldown() { if (this instanceof SinkingCraft) return type.getIntProperty(CraftType.SINK_RATE_TICKS); - if (materials.isEmpty()) { - for (MovecraftLocation location : hitBox) { - materials.add(location.toBukkit(w).getBlock().getType()); - } - } + Counter<Material> materials = getDataTag(Craft.MATERIALS); int chestPenalty = 0; - for (Material m : Tags.CHESTS) { - chestPenalty += materials.get(m); + if (!materials.isEmpty()) { + for (Material m : Tags.CHESTS) { + chestPenalty += materials.get(m); + } } - chestPenalty *= type.getDoubleProperty(CraftType.CHEST_PENALTY); + chestPenalty *= (int) type.getDoubleProperty(CraftType.CHEST_PENALTY); if (!cruising) return ((int) type.getPerWorldProperty(CraftType.PER_WORLD_TICK_COOLDOWN, w) + chestPenalty) * (type.getBoolProperty(CraftType.GEAR_SHIFTS_AFFECT_TICK_COOLDOWN) ? currentGear : 1); @@ -345,6 +339,9 @@ public int getTickCooldown() { // Dynamic Fly Block Speed int cruiseTickCooldown = (int) type.getPerWorldProperty(CraftType.PER_WORLD_CRUISE_TICK_COOLDOWN, w); if (type.getDoubleProperty(CraftType.DYNAMIC_FLY_BLOCK_SPEED_FACTOR) != 0) { + if (materials.isEmpty()) { + return ((int) type.getPerWorldProperty(CraftType.PER_WORLD_TICK_COOLDOWN, w) + chestPenalty) * (type.getBoolProperty(CraftType.GEAR_SHIFTS_AFFECT_TICK_COOLDOWN) ? currentGear : 1); + } EnumSet<Material> flyBlockMaterials = type.getMaterialSetProperty(CraftType.DYNAMIC_FLY_BLOCK); double count = 0; for (Material m : flyBlockMaterials) { @@ -536,25 +533,6 @@ public void setAudience(@NotNull Audience audience) { this.audience = audience; } - public void updateMaterials (Counter<Material> counter) { - materials = counter; - } - - @Override - public Counter<Material> getMaterials() { - return materials; - } - - @Override - public void setTotalFuel(double fuel) { - totalFuel = fuel; - } - - @Override - public double getTotalFuel() { - return totalFuel; - } - @Override public CraftDataTagContainer getDataTagContainer() { return dataTagContainer; diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsCommand.java b/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsCommand.java index 3307022c8..08e5cb1a9 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsCommand.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsCommand.java @@ -57,7 +57,7 @@ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command ComponentPaginator paginator = new ComponentPaginator( I18nSupport.getInternationalisedComponent("Contacts"), (pageNumber) -> "/contacts " + pageNumber); - for (Craft target : base.getDataTag(ContactsManager.CONTACTS)) { + for (Craft target : base.getDataTag(Craft.CONTACTS)) { if (target.getHitBox().isEmpty()) continue; diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsManager.java b/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsManager.java index 2097b4a1b..359493f47 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsManager.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsManager.java @@ -27,7 +27,6 @@ import java.util.*; public class ContactsManager extends BukkitRunnable implements Listener { - public static final CraftDataTagKey<List<Craft>> CONTACTS = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "contacts"), craft -> new ArrayList<>(0)); private static final CraftDataTagKey<Map<Craft, Long>> RECENT_CONTACTS = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "recent-contacts"), craft -> new WeakHashMap<>()); @Override @@ -52,7 +51,7 @@ private void runContacts() { } private void update(@NotNull Craft base, @NotNull Set<Craft> craftsInWorld) { - List<Craft> previousContacts = base.getDataTag(CONTACTS); + List<Craft> previousContacts = base.getDataTag(Craft.CONTACTS); if (previousContacts == null) previousContacts = new ArrayList<>(0); List<Craft> futureContacts = get(base, craftsInWorld); @@ -71,7 +70,7 @@ private void update(@NotNull Craft base, @NotNull Set<Craft> craftsInWorld) { Bukkit.getServer().getPluginManager().callEvent(event); } - base.setDataTag(CONTACTS, futureContacts); + base.setDataTag(Craft.CONTACTS, futureContacts); } private @NotNull List<Craft> get(Craft base, @NotNull Set<Craft> craftsInWorld) { @@ -126,7 +125,7 @@ private void runRecentContacts() { if (base.getHitBox().isEmpty()) continue; - for (Craft target : base.getDataTag(CONTACTS)) { + for (Craft target : base.getDataTag(Craft.CONTACTS)) { // has the craft not been seen in the last minute? if (System.currentTimeMillis() - base.getDataTag(RECENT_CONTACTS).getOrDefault(target, 0L) <= 60000) continue; @@ -235,12 +234,12 @@ public void onCraftSink(@NotNull CraftSinkEvent e) { private void remove(Craft base) { for (Craft other : CraftManager.getInstance().getCrafts()) { - List<Craft> contacts = other.getDataTag(CONTACTS); + List<Craft> contacts = other.getDataTag(Craft.CONTACTS); if (contacts.contains(base)) continue; contacts.remove(base); - other.setDataTag(CONTACTS, contacts); + other.setDataTag(Craft.CONTACTS, contacts); } for (Craft other : CraftManager.getInstance().getCrafts()) { diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsSign.java b/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsSign.java index 4bcef2711..83f52d845 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsSign.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/features/contacts/ContactsSign.java @@ -20,11 +20,6 @@ public class ContactsSign implements Listener { private static final String HEADER = "Contacts:"; - private final ContactsManager contactsManager; - - public ContactsSign(ContactsManager contactsManager) { - this.contactsManager = contactsManager; - } @EventHandler public void onCraftDetect(@NotNull CraftDetectEvent event) { @@ -55,7 +50,7 @@ public final void onSignTranslateEvent(@NotNull SignTranslateEvent event) { Craft base = event.getCraft(); int line = 1; - for (Craft target : base.getDataTag(ContactsManager.CONTACTS)) { + for (Craft target : base.getDataTag(Craft.CONTACTS)) { if (line > 3) break; diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java b/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java new file mode 100644 index 000000000..08b2b2afb --- /dev/null +++ b/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusManager.java @@ -0,0 +1,177 @@ +package net.countercraft.movecraft.features.status; + +import net.countercraft.movecraft.MovecraftLocation; +import net.countercraft.movecraft.config.Settings; +import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.craft.CraftManager; +import net.countercraft.movecraft.craft.SinkingCraft; +import net.countercraft.movecraft.craft.datatag.CraftDataTagContainer; +import net.countercraft.movecraft.craft.datatag.CraftDataTagKey; +import net.countercraft.movecraft.craft.type.CraftType; +import net.countercraft.movecraft.craft.type.RequiredBlockEntry; +import net.countercraft.movecraft.features.status.events.CraftStatusUpdateEvent; +import net.countercraft.movecraft.localisation.I18nSupport; +import net.countercraft.movecraft.processing.WorldManager; +import net.countercraft.movecraft.processing.effects.Effect; +import net.countercraft.movecraft.util.Counter; +import net.countercraft.movecraft.util.Tags; +import net.kyori.adventure.key.Key; +import net.kyori.adventure.sound.Sound; +import org.bukkit.Bukkit; +import org.bukkit.Material; +import org.bukkit.NamespacedKey; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.Listener; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.scheduler.BukkitRunnable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; +import java.util.function.Supplier; + +public class StatusManager extends BukkitRunnable implements Listener { + private static final CraftDataTagKey<Long> LAST_STATUS_CHECK = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "last-status-check"), craft -> System.currentTimeMillis()); + + @Override + public void run() { + for (Craft c : CraftManager.getInstance().getCrafts()) { + long ticksElapsed = (System.currentTimeMillis() - c.getDataTag(LAST_STATUS_CHECK)) / 50; + if (ticksElapsed <= Settings.SinkCheckTicks) + continue; + + c.setDataTag(LAST_STATUS_CHECK, System.currentTimeMillis()); + WorldManager.INSTANCE.submit(new StatusUpdateTask(c)); + } + } + + private static final class StatusUpdateTask implements Supplier<Effect> { + private final Craft craft; + private final Map<Material, Double> fuelTypes; + + private StatusUpdateTask(@NotNull Craft craft) { + this.craft = craft; + + Object object = craft.getType().getObjectProperty(CraftType.FUEL_TYPES); + if(!(object instanceof Map<?, ?> map)) + throw new IllegalStateException("FUEL_TYPES must be of type Map"); + for(var e : map.entrySet()) { + if(!(e.getKey() instanceof Material)) + throw new IllegalStateException("Keys in FUEL_TYPES must be of type Material"); + if(!(e.getValue() instanceof Double)) + throw new IllegalStateException("Values in FUEL_TYPES must be of type Double"); + } + fuelTypes = (Map<Material, Double>) map; + } + + @Override + public @Nullable Effect get() { + Counter<Material> materials = new Counter<>(); + int nonNegligibleBlocks = 0; + int nonNegligibleSolidBlocks = 0; + double fuel = 0; + for (MovecraftLocation l : craft.getHitBox()) { + Material type = craft.getMovecraftWorld().getMaterial(l); + materials.add(type); + + if (type != Material.FIRE && !type.isAir()) { + nonNegligibleBlocks++; + } + if (type != Material.FIRE && !type.isAir() && !Tags.FLUID.contains(type)) { + nonNegligibleSolidBlocks++; + } + + if (Tags.FURNACES.contains(type)) { + InventoryHolder inventoryHolder = (InventoryHolder) craft.getMovecraftWorld().getState(l); + for (ItemStack iStack : inventoryHolder.getInventory()) { + if (iStack == null || !fuelTypes.containsKey(iStack.getType())) + continue; + fuel += iStack.getAmount() * fuelTypes.get(iStack.getType()); + } + } + } + + craft.setDataTag(Craft.FUEL, fuel); + craft.setDataTag(Craft.MATERIALS, materials); + craft.setDataTag(Craft.NON_NEGLIGIBLE_BLOCKS, nonNegligibleBlocks); + craft.setDataTag(Craft.NON_NEGLIGIBLE_SOLID_BLOCKS, nonNegligibleSolidBlocks); + craft.setDataTag(LAST_STATUS_CHECK, System.currentTimeMillis()); + return () -> Bukkit.getPluginManager().callEvent(new CraftStatusUpdateEvent(craft)); + } + } + + @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) + public void onCraftStatusUpdate(@NotNull CraftStatusUpdateEvent e) { + Craft craft = e.getCraft(); + if (craft instanceof SinkingCraft) + return; + if (craft.getType().getDoubleProperty(CraftType.SINK_PERCENT) == 0.0) + return; + + boolean sinking = false; + boolean disabled = false; + Counter<Material> materials = craft.getDataTag(Craft.MATERIALS); + int nonNegligibleBlocks = craft.getDataTag(Craft.NON_NEGLIGIBLE_BLOCKS); + int nonNegligibleSolidBlocks = craft.getDataTag(Craft.NON_NEGLIGIBLE_SOLID_BLOCKS); + + // Build up counters of the fly and move blocks + Counter<RequiredBlockEntry> flyBlocks = new Counter<>(); + flyBlocks.putAll(craft.getType().getRequiredBlockProperty(CraftType.FLY_BLOCKS)); + Counter<RequiredBlockEntry> moveBlocks = new Counter<>(); + moveBlocks.putAll(craft.getType().getRequiredBlockProperty(CraftType.MOVE_BLOCKS)); + for (Material m : materials.getKeySet()) { + for (RequiredBlockEntry entry : flyBlocks.getKeySet()) { + if(entry.contains(m)) + flyBlocks.add(entry, materials.get(m)); + } + for (RequiredBlockEntry entry : moveBlocks.getKeySet()) { + if(entry.contains(m)) + moveBlocks.add(entry, materials.get(m)); + } + } + + // now see if any of the resulting percentages are below the threshold specified in sinkPercent + double sinkPercent = craft.getType().getDoubleProperty(CraftType.SINK_PERCENT) / 100.0; + for (RequiredBlockEntry entry : flyBlocks.getKeySet()) { + if(!entry.check(flyBlocks.get(entry), nonNegligibleBlocks, sinkPercent)) + sinking = true; + } + for (RequiredBlockEntry entry : moveBlocks.getKeySet()) { + if (!entry.check(moveBlocks.get(entry), nonNegligibleBlocks, sinkPercent)) + disabled = true; + } + + // And check the OverallSinkPercent + if (craft.getType().getDoubleProperty(CraftType.OVERALL_SINK_PERCENT) != 0.0) { + double percent; + if (craft.getType().getBoolProperty(CraftType.BLOCKED_BY_WATER)) { + percent = (double) nonNegligibleBlocks + / (double) craft.getOrigBlockCount(); + } + else { + percent = (double) nonNegligibleSolidBlocks + / (double) craft.getOrigBlockCount(); + } + if (percent * 100.0 < craft.getType().getDoubleProperty(CraftType.OVERALL_SINK_PERCENT)) + sinking = true; + } + + if (nonNegligibleBlocks == 0) + sinking = true; + + // If the craft is disabled, play a sound and disable it. + if (disabled && !craft.getDisabled()) { + craft.setDisabled(true); + craft.getAudience().playSound(Sound.sound(Key.key("entity.iron_golem.death"), Sound.Source.NEUTRAL, 5.0f, 5.0f)); + } + + // If the craft is sinking, let the player know and sink the craft. + if (sinking) { + craft.getAudience().sendMessage(I18nSupport.getInternationalisedComponent("Player - Craft is sinking")); + craft.setCruising(false); + CraftManager.getInstance().sink(craft); + } + } +} diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/sign/StatusSign.java b/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusSign.java similarity index 96% rename from Movecraft/src/main/java/net/countercraft/movecraft/sign/StatusSign.java rename to Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusSign.java index f7ab7df41..1246c1381 100644 --- a/Movecraft/src/main/java/net/countercraft/movecraft/sign/StatusSign.java +++ b/Movecraft/src/main/java/net/countercraft/movecraft/features/status/StatusSign.java @@ -1,4 +1,4 @@ -package net.countercraft.movecraft.sign; +package net.countercraft.movecraft.features.status; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; @@ -10,6 +10,7 @@ import net.countercraft.movecraft.craft.type.RequiredBlockEntry; import net.countercraft.movecraft.events.CraftDetectEvent; import net.countercraft.movecraft.events.SignTranslateEvent; +import net.countercraft.movecraft.features.status.StatusManager; import net.countercraft.movecraft.util.Counter; import net.countercraft.movecraft.util.Tags; import org.bukkit.ChatColor; @@ -63,11 +64,11 @@ public final void onSignTranslate(SignTranslateEvent event) { if (!ChatColor.stripColor(event.getLine(0)).equalsIgnoreCase("Status:")) { return; } - double fuel = craft.getTotalFuel(); + double fuel = craft.getDataTag(Craft.FUEL); int totalNonNegligibleBlocks = 0; int totalNonNegligibleWaterBlocks = 0; - Counter<Material> materials = craft.getMaterials(); + Counter<Material> materials = craft.getDataTag(Craft.MATERIALS); if (materials.isEmpty()) { return; } diff --git a/Movecraft/src/main/java/net/countercraft/movecraft/features/status/events/CraftStatusUpdateEvent.java b/Movecraft/src/main/java/net/countercraft/movecraft/features/status/events/CraftStatusUpdateEvent.java new file mode 100644 index 000000000..ea5d5588d --- /dev/null +++ b/Movecraft/src/main/java/net/countercraft/movecraft/features/status/events/CraftStatusUpdateEvent.java @@ -0,0 +1,24 @@ +package net.countercraft.movecraft.features.status.events; + +import net.countercraft.movecraft.craft.Craft; +import net.countercraft.movecraft.events.CraftEvent; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; + +public class CraftStatusUpdateEvent extends CraftEvent { + private static final HandlerList HANDLERS = new HandlerList(); + + public CraftStatusUpdateEvent(@NotNull Craft craft) { + super(craft); + } + + @SuppressWarnings("unused") + public static HandlerList getHandlerList() { + return HANDLERS; + } + + @Override + public HandlerList getHandlers() { + return HANDLERS; + } +} diff --git a/api/src/main/java/net/countercraft/movecraft/craft/Craft.java b/api/src/main/java/net/countercraft/movecraft/craft/Craft.java index f53aa96a0..99af4910d 100644 --- a/api/src/main/java/net/countercraft/movecraft/craft/Craft.java +++ b/api/src/main/java/net/countercraft/movecraft/craft/Craft.java @@ -30,14 +30,22 @@ import net.kyori.adventure.audience.Audience; import org.bukkit.Location; import org.bukkit.Material; +import org.bukkit.NamespacedKey; import org.bukkit.World; import org.bukkit.block.Sign; import org.bukkit.block.data.BlockData; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.List; import java.util.Map; public interface Craft { + CraftDataTagKey<List<Craft>> CONTACTS = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "contacts"), craft -> new ArrayList<>(0)); + CraftDataTagKey<Double> FUEL = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "fuel"), craft -> 0D); + CraftDataTagKey<Counter<Material>> MATERIALS = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "materials"), craft -> new Counter<>()); + CraftDataTagKey<Integer> NON_NEGLIGIBLE_BLOCKS = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "non-negligible-blocks"), Craft::getOrigBlockCount); + CraftDataTagKey<Integer> NON_NEGLIGIBLE_SOLID_BLOCKS = CraftDataTagContainer.tryRegisterTagKey(new NamespacedKey("movecraft", "non-negligible-solid-blocks"), Craft::getOrigBlockCount); @Deprecated boolean isNotProcessing(); @@ -249,14 +257,6 @@ default void setLastDZ(int dZ){} void setAudience(Audience audience); - Counter<Material> getMaterials (); - - void updateMaterials (Counter<Material> materials); - - double getTotalFuel (); - - void setTotalFuel (double fuel); - public default CraftDataTagContainer getDataTagContainer() { return null; } diff --git a/api/src/main/java/net/countercraft/movecraft/craft/CraftStatus.java b/api/src/main/java/net/countercraft/movecraft/craft/CraftStatus.java deleted file mode 100644 index 5e16ac713..000000000 --- a/api/src/main/java/net/countercraft/movecraft/craft/CraftStatus.java +++ /dev/null @@ -1,29 +0,0 @@ -package net.countercraft.movecraft.craft; - -public enum CraftStatus { - NORMAL, - SINKING, - DISABLED, - SINKING_DISABLED; - - public boolean isSinking() { - return (this == SINKING || this == SINKING_DISABLED); - } - - public boolean isDisabled() { - return (this == DISABLED || this == SINKING_DISABLED); - } - - public static CraftStatus of (boolean sinking, boolean disabled) { - if (!sinking && !disabled) { - return NORMAL; - } - if (!disabled) { - return SINKING; - } - if (!sinking) { - return DISABLED; - } - return SINKING_DISABLED; - } -} \ No newline at end of file diff --git a/api/src/main/java/net/countercraft/movecraft/processing/WorldManager.java b/api/src/main/java/net/countercraft/movecraft/processing/WorldManager.java index d0db41e65..ecef2e3df 100644 --- a/api/src/main/java/net/countercraft/movecraft/processing/WorldManager.java +++ b/api/src/main/java/net/countercraft/movecraft/processing/WorldManager.java @@ -45,9 +45,10 @@ public void run() { if(tasks.isEmpty()) return; running = true; - int remaining = tasks.size(); + int remaining = 0; List<CompletableFuture<Effect>> inProgress = new ArrayList<>(); while(!tasks.isEmpty()){ + remaining++; inProgress.add(CompletableFuture.supplyAsync(tasks.poll()).whenComplete((effect, exception) -> { poison(); if(exception != null){ diff --git a/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts b/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts index 9087d9dbe..91e1dbe0f 100644 --- a/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/buildlogic.java-conventions.gradle.kts @@ -11,7 +11,7 @@ repositories { } group = "net.countercraft" -version = "8.0.0_beta-4" +version = "8.0.0_beta-5_dev-1" tasks.withType<JavaCompile>() { options.encoding = "UTF-8"