Skip to content

Commit

Permalink
Craft status rewrite (#674)
Browse files Browse the repository at this point in the history
* Stat rewrite

* More WIP

* WIP

* Clean up

* Update buildlogic.java-conventions.gradle.kts

* Update StatusManager.java

* Update StatusManager.java

* Update Movecraft.java

* Call event as an effect

* Fix race condition in WorldManager

* Fix multiple status updates

* Fix speed
  • Loading branch information
TylerS1066 authored Jul 27, 2024
1 parent 09f23f9 commit e04b661
Show file tree
Hide file tree
Showing 13 changed files with 242 additions and 237 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -217,17 +219,21 @@ 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);

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.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -440,7 +385,6 @@ public void run() {
clearAll();

processCruise();
detectSinking();
processSinking();
processFadingBlocks();
processAlgorithmQueue();
Expand Down Expand Up @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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();
}

Expand Down Expand Up @@ -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);

Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand All @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;

Expand Down
Loading

0 comments on commit e04b661

Please sign in to comment.