feat(chunkloader): add fuel charging, duration timers, and /chunkloader tempo
This commit is contained in:
@@ -1826,6 +1826,7 @@ final class CanalhandiaCommand implements CommandExecutor, TabCompleter {
|
||||
case "nome", "renomear", "rename" -> chunkloaderRename(sender, rest);
|
||||
case "ativar", "enable" -> chunkloaderSetEnabled(sender, rest, true);
|
||||
case "desativar", "pausar", "disable", "pause" -> chunkloaderSetEnabled(sender, rest, false);
|
||||
case "tempo", "abastecer", "fuel" -> chunkloaderTime(sender, rest);
|
||||
case "admin" -> chunkloaderAdmin(sender, rest);
|
||||
case "dar", "give" -> {
|
||||
if (admin(sender)) {
|
||||
@@ -1863,23 +1864,24 @@ final class CanalhandiaCommand implements CommandExecutor, TabCompleter {
|
||||
|
||||
Component line = Component.text(" " + loader.displayName() + " ", NamedTextColor.AQUA)
|
||||
.append(Component.text(loader.place() + " " + loader.chunkCoords() + " ", NamedTextColor.WHITE))
|
||||
.append(Component.text(statusText + " ", statusColor));
|
||||
.append(Component.text(statusText + " ", statusColor))
|
||||
.append(Component.text("[" + loader.timeLeft() + "]", NamedTextColor.GOLD));
|
||||
|
||||
if (!bedrock) {
|
||||
if (loader.enabled()) {
|
||||
line = line.append(Component.text("[Pausar] ", NamedTextColor.YELLOW)
|
||||
line = line.append(Component.text(" [Pausar]", NamedTextColor.YELLOW)
|
||||
.hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(
|
||||
Component.text("Clique para pausar " + loader.displayName(), NamedTextColor.GRAY)))
|
||||
.clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand(
|
||||
"/chunkloader desativar " + loader.id())));
|
||||
} else {
|
||||
line = line.append(Component.text("[Ativar] ", NamedTextColor.GREEN)
|
||||
line = line.append(Component.text(" [Ativar]", NamedTextColor.GREEN)
|
||||
.hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(
|
||||
Component.text("Clique para reativar " + loader.displayName(), NamedTextColor.GRAY)))
|
||||
.clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand(
|
||||
"/chunkloader ativar " + loader.id())));
|
||||
}
|
||||
line = line.append(Component.text("[Remover]", NamedTextColor.RED)
|
||||
line = line.append(Component.text(" [Remover]", NamedTextColor.RED)
|
||||
.hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(
|
||||
Component.text("Clique para recolher " + loader.displayName(), NamedTextColor.GRAY)))
|
||||
.clickEvent(net.kyori.adventure.text.event.ClickEvent.suggestCommand(
|
||||
@@ -1916,6 +1918,8 @@ final class CanalhandiaCommand implements CommandExecutor, TabCompleter {
|
||||
sender.sendMessage(Component.text(" Status: ", NamedTextColor.GRAY)
|
||||
.append(Component.text(here.displayName() + " (" + status + ")", statusCol))
|
||||
.append(Component.text(" de " + here.ownerName(), NamedTextColor.WHITE)));
|
||||
sender.sendMessage(Component.text(" Tempo restante: ", NamedTextColor.GRAY)
|
||||
.append(Component.text(here.timeLeft(), NamedTextColor.GOLD)));
|
||||
} else {
|
||||
sender.sendMessage(Component.text(" Status: ", NamedTextColor.GRAY)
|
||||
.append(Component.text("Carregamento normal (sem âncora)", NamedTextColor.DARK_GRAY)));
|
||||
@@ -1976,6 +1980,40 @@ final class CanalhandiaCommand implements CommandExecutor, TabCompleter {
|
||||
Msg.ok(sender, "Âncora " + loader.displayName() + " " + (enabled ? "ATIVADA." : "PAUSADA/DESATIVADA."));
|
||||
}
|
||||
|
||||
private void chunkloaderTime(CommandSender sender, String[] args) {
|
||||
if (args.length < 2) {
|
||||
Msg.error(sender, "Uso: /chunkloader tempo <id|nome> <horas>");
|
||||
return;
|
||||
}
|
||||
ChunkLoader loader = plugin.chunkLoaders().find(args[0]);
|
||||
if (loader == null) {
|
||||
Msg.error(sender, "Nenhuma âncora encontrada para '" + args[0] + "'.");
|
||||
return;
|
||||
}
|
||||
boolean isOwner = sender instanceof Player player && player.getUniqueId().toString().equalsIgnoreCase(loader.ownerUuid());
|
||||
boolean isAdmin = sender.hasPermission(ADMIN) || sender.hasPermission("canalhandia.chunkloader.admin");
|
||||
if (!isOwner && !isAdmin) {
|
||||
Msg.error(sender, "Esta âncora pertence a " + loader.ownerName() + ".");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
double hours = Double.parseDouble(args[1]);
|
||||
if (hours <= 0 && isAdmin) {
|
||||
plugin.chunkLoaders().addTime(loader.id(), -loader.expiresAt()); // permanent
|
||||
Msg.ok(sender, "Âncora " + loader.displayName() + " definida como PERMANENTE.");
|
||||
} else {
|
||||
long millis = (long) (hours * 3600_000L);
|
||||
plugin.chunkLoaders().addTime(loader.id(), millis);
|
||||
ChunkLoader updated = plugin.chunkLoaders().byId(loader.id());
|
||||
String time = (updated != null) ? updated.timeLeft() : "Permanente";
|
||||
Msg.ok(sender, "Adicionado " + hours + "h à âncora " + loader.displayName() + ". Tempo restante: " + time);
|
||||
}
|
||||
plugin.blueMap().syncChunkLoaders();
|
||||
} catch (NumberFormatException e) {
|
||||
Msg.error(sender, "Horas inválidas: " + args[1]);
|
||||
}
|
||||
}
|
||||
|
||||
private void chunkloaderRemove(CommandSender sender, String[] args) {
|
||||
if (args.length < 1) {
|
||||
Msg.error(sender, "Uso: /chunkloader remover <id|nome>");
|
||||
@@ -2187,6 +2225,8 @@ final class CanalhandiaCommand implements CommandExecutor, TabCompleter {
|
||||
.append(Component.text(" — Desativa e recolhe uma de suas âncoras", NamedTextColor.GRAY)));
|
||||
sender.sendMessage(Component.text(" /chunkloader receita", NamedTextColor.AQUA)
|
||||
.append(Component.text(" — Mostra a receita de criação do bloco", NamedTextColor.GRAY)));
|
||||
sender.sendMessage(Component.text(" /chunkloader tempo <id|nome> <horas>", NamedTextColor.AQUA)
|
||||
.append(Component.text(" — Adiciona tempo ou abastece a âncora", NamedTextColor.GRAY)));
|
||||
sender.sendMessage(Component.text(" /chunkloader limite", NamedTextColor.AQUA)
|
||||
.append(Component.text(" — Mostra ou configura os limites de âncoras", NamedTextColor.GRAY)));
|
||||
if (sender.hasPermission(ADMIN) || sender.hasPermission("canalhandia.chunkloader.admin")) {
|
||||
@@ -2358,7 +2398,7 @@ final class CanalhandiaCommand implements CommandExecutor, TabCompleter {
|
||||
}
|
||||
if (name.equals("chunkloader") || name.equals("ancora") || name.equals("ancoras")) {
|
||||
if (args.length == 1) {
|
||||
List<String> options = new ArrayList<>(List.of("info", "listar", "nome", "ativar", "desativar", "remover", "receita", "limite"));
|
||||
List<String> options = new ArrayList<>(List.of("info", "listar", "nome", "ativar", "desativar", "tempo", "remover", "receita", "limite"));
|
||||
if (sender.hasPermission(ADMIN) || sender.hasPermission("canalhandia.chunkloader.admin")) {
|
||||
options.addAll(List.of("admin", "dar"));
|
||||
}
|
||||
@@ -2367,7 +2407,8 @@ final class CanalhandiaCommand implements CommandExecutor, TabCompleter {
|
||||
if (args.length == 2 && (args[0].equalsIgnoreCase("remover") || args[0].equalsIgnoreCase("del")
|
||||
|| args[0].equalsIgnoreCase("nome") || args[0].equalsIgnoreCase("renomear")
|
||||
|| args[0].equalsIgnoreCase("ativar") || args[0].equalsIgnoreCase("desativar")
|
||||
|| args[0].equalsIgnoreCase("pausar"))) {
|
||||
|| args[0].equalsIgnoreCase("pausar") || args[0].equalsIgnoreCase("tempo")
|
||||
|| args[0].equalsIgnoreCase("abastecer"))) {
|
||||
if (sender instanceof Player player) {
|
||||
List<String> identifiers = new ArrayList<>();
|
||||
for (ChunkLoader loader : plugin.chunkLoaders().byOwner(player.getUniqueId().toString())) {
|
||||
|
||||
@@ -10,6 +10,7 @@ record ChunkLoader(
|
||||
long id,
|
||||
String name,
|
||||
boolean enabled,
|
||||
long expiresAt,
|
||||
String ownerUuid,
|
||||
String ownerName,
|
||||
String world,
|
||||
@@ -21,7 +22,11 @@ record ChunkLoader(
|
||||
long createdAt) {
|
||||
|
||||
ChunkLoader(long id, String ownerUuid, String ownerName, String world, int x, int y, int z, int chunkX, int chunkZ, long createdAt) {
|
||||
this(id, "", true, ownerUuid, ownerName, world, x, y, z, chunkX, chunkZ, createdAt);
|
||||
this(id, "", true, 0L, ownerUuid, ownerName, world, x, y, z, chunkX, chunkZ, createdAt);
|
||||
}
|
||||
|
||||
ChunkLoader(long id, String name, boolean enabled, String ownerUuid, String ownerName, String world, int x, int y, int z, int chunkX, int chunkZ, long createdAt) {
|
||||
this(id, name, enabled, 0L, ownerUuid, ownerName, world, x, y, z, chunkX, chunkZ, createdAt);
|
||||
}
|
||||
|
||||
String blockCoords() {
|
||||
@@ -44,11 +49,37 @@ record ChunkLoader(
|
||||
return (name != null && !name.isBlank()) ? name : "#" + id;
|
||||
}
|
||||
|
||||
boolean isExpired() {
|
||||
return expiresAt > 0 && System.currentTimeMillis() >= expiresAt;
|
||||
}
|
||||
|
||||
String timeLeft() {
|
||||
if (expiresAt <= 0) {
|
||||
return "Permanente";
|
||||
}
|
||||
long diff = expiresAt - System.currentTimeMillis();
|
||||
if (diff <= 0) {
|
||||
return "Expirado";
|
||||
}
|
||||
long hours = diff / (3600_000L);
|
||||
long minutes = (diff % (3600_000L)) / 60_000L;
|
||||
if (hours >= 24) {
|
||||
long days = hours / 24;
|
||||
hours = hours % 24;
|
||||
return days + "d " + hours + "h";
|
||||
}
|
||||
return hours + "h " + minutes + "m";
|
||||
}
|
||||
|
||||
ChunkLoader withName(String newName) {
|
||||
return new ChunkLoader(id, newName == null ? "" : newName.trim(), enabled, ownerUuid, ownerName, world, x, y, z, chunkX, chunkZ, createdAt);
|
||||
return new ChunkLoader(id, newName == null ? "" : newName.trim(), enabled, expiresAt, ownerUuid, ownerName, world, x, y, z, chunkX, chunkZ, createdAt);
|
||||
}
|
||||
|
||||
ChunkLoader withEnabled(boolean newEnabled) {
|
||||
return new ChunkLoader(id, name, newEnabled, ownerUuid, ownerName, world, x, y, z, chunkX, chunkZ, createdAt);
|
||||
return new ChunkLoader(id, name, newEnabled, expiresAt, ownerUuid, ownerName, world, x, y, z, chunkX, chunkZ, createdAt);
|
||||
}
|
||||
|
||||
ChunkLoader withExpiresAt(long newExpiresAt) {
|
||||
return new ChunkLoader(id, name, enabled, Math.max(0L, newExpiresAt), ownerUuid, ownerName, world, x, y, z, chunkX, chunkZ, createdAt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +188,26 @@ final class ChunkLoaderListener implements Listener {
|
||||
boolean isOwner = player.getUniqueId().toString().equalsIgnoreCase(loader.ownerUuid());
|
||||
boolean isAdmin = player.hasPermission("canalhandia.admin") || player.hasPermission("canalhandia.chunkloader.admin");
|
||||
|
||||
ItemStack inHand = player.getInventory().getItemInMainHand();
|
||||
long fuelMillis = fuelDuration(inHand.getType());
|
||||
if (fuelMillis > 0 && (isOwner || isAdmin)) {
|
||||
if (player.getGameMode() != GameMode.CREATIVE) {
|
||||
inHand.subtract(1);
|
||||
}
|
||||
plugin.chunkLoaders().addTime(loader.id(), fuelMillis);
|
||||
ChunkLoader updated = plugin.chunkLoaders().byId(loader.id());
|
||||
Location l = block.getLocation().add(0.5, 0.5, 0.5);
|
||||
try {
|
||||
l.getWorld().playSound(l, Sound.BLOCK_RESPAWN_ANCHOR_CHARGE, 1.0f, 1.2f);
|
||||
l.getWorld().spawnParticle(Particle.FIREWORK, l, 15, 0.2, 0.2, 0.2, 0.05);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
plugin.blueMap().syncChunkLoaders();
|
||||
String time = (updated != null) ? updated.timeLeft() : "Permanente";
|
||||
Msg.ok(player, "Âncora " + loader.displayName() + " abastecida! Tempo restante: " + time);
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.isSneaking() && (isOwner || isAdmin)) {
|
||||
boolean newState = !loader.enabled();
|
||||
plugin.chunkLoaders().setEnabled(loader.id(), newState);
|
||||
@@ -212,10 +232,30 @@ final class ChunkLoaderListener implements Listener {
|
||||
}
|
||||
|
||||
String stateStr = loader.enabled() ? "ATIVA" : "PAUSADA";
|
||||
Msg.ok(player, "Âncora " + loader.displayName() + " (" + loader.ownerName() + ") está " + stateStr + ". (Shift + Clique Direito para alternar)");
|
||||
Msg.ok(player, "Âncora " + loader.displayName() + " (" + loader.ownerName() + ") está " + stateStr
|
||||
+ " [Tempo: " + loader.timeLeft() + "]. (Shift + Clique Direito para pausar/ativar | Clique com combustível para abastecer)");
|
||||
}
|
||||
}
|
||||
|
||||
private static long fuelDuration(Material type) {
|
||||
if (type == Material.REDSTONE || type == Material.GLOWSTONE_DUST) {
|
||||
return 30 * 60_000L; // 30 min
|
||||
}
|
||||
if (type == Material.GLOWSTONE || type == Material.AMETHYST_SHARD) {
|
||||
return 2 * 3600_000L; // 2h
|
||||
}
|
||||
if (type == Material.ENDER_PEARL || type == Material.BLAZE_POWDER || type == Material.BLAZE_ROD) {
|
||||
return 4 * 3600_000L; // 4h
|
||||
}
|
||||
if (type == Material.ENDER_EYE || type == Material.DIAMOND || type == Material.EMERALD) {
|
||||
return 12 * 3600_000L; // 12h
|
||||
}
|
||||
if (type == Material.NETHER_STAR || type == Material.NETHERITE_INGOT || type == Material.END_CRYSTAL) {
|
||||
return 7 * 24 * 3600_000L; // 7 dias
|
||||
}
|
||||
return 0L;
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||
public void onBlockExplode(BlockExplodeEvent event) {
|
||||
protectExplosions(event.blockList().iterator());
|
||||
|
||||
@@ -71,6 +71,7 @@ final class ChunkLoaders {
|
||||
long id = Long.parseLong(key);
|
||||
String name = root.getString(key + ".name", "");
|
||||
boolean enabled = root.getBoolean(key + ".enabled", true);
|
||||
long expiresAt = root.getLong(key + ".expires_at", 0L);
|
||||
String ownerUuid = root.getString(key + ".owner_uuid", "");
|
||||
String ownerName = root.getString(key + ".owner_name", "?");
|
||||
String world = root.getString(key + ".world", "world");
|
||||
@@ -81,7 +82,7 @@ final class ChunkLoaders {
|
||||
int chunkZ = root.getInt(key + ".chunk_z", z >> 4);
|
||||
long createdAt = root.getLong(key + ".created_at", System.currentTimeMillis());
|
||||
|
||||
loaders.add(new ChunkLoader(id, name, enabled, ownerUuid, ownerName, world, x, y, z, chunkX, chunkZ, createdAt));
|
||||
loaders.add(new ChunkLoader(id, name, enabled, expiresAt, ownerUuid, ownerName, world, x, y, z, chunkX, chunkZ, createdAt));
|
||||
if (id > maxId) {
|
||||
maxId = id;
|
||||
}
|
||||
@@ -96,14 +97,18 @@ final class ChunkLoaders {
|
||||
* Registers a new chunk loader and activates the ticket in the world.
|
||||
*/
|
||||
ChunkLoader add(String ownerUuid, String ownerName, String world, int x, int y, int z) {
|
||||
return add(ownerUuid, ownerName, world, x, y, z, "");
|
||||
return add(ownerUuid, ownerName, world, x, y, z, "", 0L);
|
||||
}
|
||||
|
||||
ChunkLoader add(String ownerUuid, String ownerName, String world, int x, int y, int z, String name) {
|
||||
return add(ownerUuid, ownerName, world, x, y, z, name, 0L);
|
||||
}
|
||||
|
||||
ChunkLoader add(String ownerUuid, String ownerName, String world, int x, int y, int z, String name, long expiresAt) {
|
||||
int chunkX = x >> 4;
|
||||
int chunkZ = z >> 4;
|
||||
long id = nextId.getAndIncrement();
|
||||
ChunkLoader loader = new ChunkLoader(id, name == null ? "" : name.trim(), true, ownerUuid, ownerName, world, x, y, z, chunkX, chunkZ, System.currentTimeMillis());
|
||||
ChunkLoader loader = new ChunkLoader(id, name == null ? "" : name.trim(), true, expiresAt, ownerUuid, ownerName, world, x, y, z, chunkX, chunkZ, System.currentTimeMillis());
|
||||
|
||||
synchronized (loaders) {
|
||||
loaders.add(loader);
|
||||
@@ -113,6 +118,32 @@ final class ChunkLoaders {
|
||||
return loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds duration (in milliseconds) to a chunk loader's expiration timer.
|
||||
*/
|
||||
boolean addTime(long id, long millisToAdd) {
|
||||
ChunkLoader updated = null;
|
||||
synchronized (loaders) {
|
||||
for (int i = 0; i < loaders.size(); i++) {
|
||||
ChunkLoader curr = loaders.get(i);
|
||||
if (curr.id() == id) {
|
||||
long now = System.currentTimeMillis();
|
||||
long base = (curr.expiresAt() > now) ? curr.expiresAt() : now;
|
||||
long newExpires = base + millisToAdd;
|
||||
updated = curr.withExpiresAt(newExpires).withEnabled(true);
|
||||
loaders.set(i, updated);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (updated != null) {
|
||||
addTicket(updated);
|
||||
save();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames an existing chunk loader.
|
||||
*/
|
||||
@@ -337,6 +368,10 @@ final class ChunkLoaders {
|
||||
}
|
||||
|
||||
for (ChunkLoader loader : currentLoaders) {
|
||||
if (loader.isExpired() && loader.enabled()) {
|
||||
setEnabled(loader.id(), false);
|
||||
continue;
|
||||
}
|
||||
if (!loader.enabled()) {
|
||||
continue;
|
||||
}
|
||||
@@ -480,6 +515,7 @@ final class ChunkLoaders {
|
||||
String key = "loaders." + l.id();
|
||||
yaml.set(key + ".name", l.name());
|
||||
yaml.set(key + ".enabled", l.enabled());
|
||||
yaml.set(key + ".expires_at", l.expiresAt());
|
||||
yaml.set(key + ".owner_uuid", l.ownerUuid());
|
||||
yaml.set(key + ".owner_name", l.ownerName());
|
||||
yaml.set(key + ".world", l.world());
|
||||
|
||||
Reference in New Issue
Block a user