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 "nome", "renomear", "rename" -> chunkloaderRename(sender, rest);
|
||||||
case "ativar", "enable" -> chunkloaderSetEnabled(sender, rest, true);
|
case "ativar", "enable" -> chunkloaderSetEnabled(sender, rest, true);
|
||||||
case "desativar", "pausar", "disable", "pause" -> chunkloaderSetEnabled(sender, rest, false);
|
case "desativar", "pausar", "disable", "pause" -> chunkloaderSetEnabled(sender, rest, false);
|
||||||
|
case "tempo", "abastecer", "fuel" -> chunkloaderTime(sender, rest);
|
||||||
case "admin" -> chunkloaderAdmin(sender, rest);
|
case "admin" -> chunkloaderAdmin(sender, rest);
|
||||||
case "dar", "give" -> {
|
case "dar", "give" -> {
|
||||||
if (admin(sender)) {
|
if (admin(sender)) {
|
||||||
@@ -1863,23 +1864,24 @@ final class CanalhandiaCommand implements CommandExecutor, TabCompleter {
|
|||||||
|
|
||||||
Component line = Component.text(" " + loader.displayName() + " ", NamedTextColor.AQUA)
|
Component line = Component.text(" " + loader.displayName() + " ", NamedTextColor.AQUA)
|
||||||
.append(Component.text(loader.place() + " " + loader.chunkCoords() + " ", NamedTextColor.WHITE))
|
.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 (!bedrock) {
|
||||||
if (loader.enabled()) {
|
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(
|
.hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(
|
||||||
Component.text("Clique para pausar " + loader.displayName(), NamedTextColor.GRAY)))
|
Component.text("Clique para pausar " + loader.displayName(), NamedTextColor.GRAY)))
|
||||||
.clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand(
|
.clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand(
|
||||||
"/chunkloader desativar " + loader.id())));
|
"/chunkloader desativar " + loader.id())));
|
||||||
} else {
|
} 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(
|
.hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(
|
||||||
Component.text("Clique para reativar " + loader.displayName(), NamedTextColor.GRAY)))
|
Component.text("Clique para reativar " + loader.displayName(), NamedTextColor.GRAY)))
|
||||||
.clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand(
|
.clickEvent(net.kyori.adventure.text.event.ClickEvent.runCommand(
|
||||||
"/chunkloader ativar " + loader.id())));
|
"/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(
|
.hoverEvent(net.kyori.adventure.text.event.HoverEvent.showText(
|
||||||
Component.text("Clique para recolher " + loader.displayName(), NamedTextColor.GRAY)))
|
Component.text("Clique para recolher " + loader.displayName(), NamedTextColor.GRAY)))
|
||||||
.clickEvent(net.kyori.adventure.text.event.ClickEvent.suggestCommand(
|
.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)
|
sender.sendMessage(Component.text(" Status: ", NamedTextColor.GRAY)
|
||||||
.append(Component.text(here.displayName() + " (" + status + ")", statusCol))
|
.append(Component.text(here.displayName() + " (" + status + ")", statusCol))
|
||||||
.append(Component.text(" de " + here.ownerName(), NamedTextColor.WHITE)));
|
.append(Component.text(" de " + here.ownerName(), NamedTextColor.WHITE)));
|
||||||
|
sender.sendMessage(Component.text(" Tempo restante: ", NamedTextColor.GRAY)
|
||||||
|
.append(Component.text(here.timeLeft(), NamedTextColor.GOLD)));
|
||||||
} else {
|
} else {
|
||||||
sender.sendMessage(Component.text(" Status: ", NamedTextColor.GRAY)
|
sender.sendMessage(Component.text(" Status: ", NamedTextColor.GRAY)
|
||||||
.append(Component.text("Carregamento normal (sem âncora)", NamedTextColor.DARK_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."));
|
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) {
|
private void chunkloaderRemove(CommandSender sender, String[] args) {
|
||||||
if (args.length < 1) {
|
if (args.length < 1) {
|
||||||
Msg.error(sender, "Uso: /chunkloader remover <id|nome>");
|
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)));
|
.append(Component.text(" — Desativa e recolhe uma de suas âncoras", NamedTextColor.GRAY)));
|
||||||
sender.sendMessage(Component.text(" /chunkloader receita", NamedTextColor.AQUA)
|
sender.sendMessage(Component.text(" /chunkloader receita", NamedTextColor.AQUA)
|
||||||
.append(Component.text(" — Mostra a receita de criação do bloco", NamedTextColor.GRAY)));
|
.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)
|
sender.sendMessage(Component.text(" /chunkloader limite", NamedTextColor.AQUA)
|
||||||
.append(Component.text(" — Mostra ou configura os limites de âncoras", NamedTextColor.GRAY)));
|
.append(Component.text(" — Mostra ou configura os limites de âncoras", NamedTextColor.GRAY)));
|
||||||
if (sender.hasPermission(ADMIN) || sender.hasPermission("canalhandia.chunkloader.admin")) {
|
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 (name.equals("chunkloader") || name.equals("ancora") || name.equals("ancoras")) {
|
||||||
if (args.length == 1) {
|
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")) {
|
if (sender.hasPermission(ADMIN) || sender.hasPermission("canalhandia.chunkloader.admin")) {
|
||||||
options.addAll(List.of("admin", "dar"));
|
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")
|
if (args.length == 2 && (args[0].equalsIgnoreCase("remover") || args[0].equalsIgnoreCase("del")
|
||||||
|| args[0].equalsIgnoreCase("nome") || args[0].equalsIgnoreCase("renomear")
|
|| args[0].equalsIgnoreCase("nome") || args[0].equalsIgnoreCase("renomear")
|
||||||
|| args[0].equalsIgnoreCase("ativar") || args[0].equalsIgnoreCase("desativar")
|
|| 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) {
|
if (sender instanceof Player player) {
|
||||||
List<String> identifiers = new ArrayList<>();
|
List<String> identifiers = new ArrayList<>();
|
||||||
for (ChunkLoader loader : plugin.chunkLoaders().byOwner(player.getUniqueId().toString())) {
|
for (ChunkLoader loader : plugin.chunkLoaders().byOwner(player.getUniqueId().toString())) {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ record ChunkLoader(
|
|||||||
long id,
|
long id,
|
||||||
String name,
|
String name,
|
||||||
boolean enabled,
|
boolean enabled,
|
||||||
|
long expiresAt,
|
||||||
String ownerUuid,
|
String ownerUuid,
|
||||||
String ownerName,
|
String ownerName,
|
||||||
String world,
|
String world,
|
||||||
@@ -21,7 +22,11 @@ record ChunkLoader(
|
|||||||
long createdAt) {
|
long createdAt) {
|
||||||
|
|
||||||
ChunkLoader(long id, String ownerUuid, String ownerName, String world, int x, int y, int z, int chunkX, int chunkZ, 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() {
|
String blockCoords() {
|
||||||
@@ -44,11 +49,37 @@ record ChunkLoader(
|
|||||||
return (name != null && !name.isBlank()) ? name : "#" + id;
|
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) {
|
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) {
|
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 isOwner = player.getUniqueId().toString().equalsIgnoreCase(loader.ownerUuid());
|
||||||
boolean isAdmin = player.hasPermission("canalhandia.admin") || player.hasPermission("canalhandia.chunkloader.admin");
|
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)) {
|
if (player.isSneaking() && (isOwner || isAdmin)) {
|
||||||
boolean newState = !loader.enabled();
|
boolean newState = !loader.enabled();
|
||||||
plugin.chunkLoaders().setEnabled(loader.id(), newState);
|
plugin.chunkLoaders().setEnabled(loader.id(), newState);
|
||||||
@@ -212,10 +232,30 @@ final class ChunkLoaderListener implements Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String stateStr = loader.enabled() ? "ATIVA" : "PAUSADA";
|
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)
|
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||||
public void onBlockExplode(BlockExplodeEvent event) {
|
public void onBlockExplode(BlockExplodeEvent event) {
|
||||||
protectExplosions(event.blockList().iterator());
|
protectExplosions(event.blockList().iterator());
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ final class ChunkLoaders {
|
|||||||
long id = Long.parseLong(key);
|
long id = Long.parseLong(key);
|
||||||
String name = root.getString(key + ".name", "");
|
String name = root.getString(key + ".name", "");
|
||||||
boolean enabled = root.getBoolean(key + ".enabled", true);
|
boolean enabled = root.getBoolean(key + ".enabled", true);
|
||||||
|
long expiresAt = root.getLong(key + ".expires_at", 0L);
|
||||||
String ownerUuid = root.getString(key + ".owner_uuid", "");
|
String ownerUuid = root.getString(key + ".owner_uuid", "");
|
||||||
String ownerName = root.getString(key + ".owner_name", "?");
|
String ownerName = root.getString(key + ".owner_name", "?");
|
||||||
String world = root.getString(key + ".world", "world");
|
String world = root.getString(key + ".world", "world");
|
||||||
@@ -81,7 +82,7 @@ final class ChunkLoaders {
|
|||||||
int chunkZ = root.getInt(key + ".chunk_z", z >> 4);
|
int chunkZ = root.getInt(key + ".chunk_z", z >> 4);
|
||||||
long createdAt = root.getLong(key + ".created_at", System.currentTimeMillis());
|
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) {
|
if (id > maxId) {
|
||||||
maxId = id;
|
maxId = id;
|
||||||
}
|
}
|
||||||
@@ -96,14 +97,18 @@ final class ChunkLoaders {
|
|||||||
* Registers a new chunk loader and activates the ticket in the world.
|
* 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) {
|
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) {
|
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 chunkX = x >> 4;
|
||||||
int chunkZ = z >> 4;
|
int chunkZ = z >> 4;
|
||||||
long id = nextId.getAndIncrement();
|
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) {
|
synchronized (loaders) {
|
||||||
loaders.add(loader);
|
loaders.add(loader);
|
||||||
@@ -113,6 +118,32 @@ final class ChunkLoaders {
|
|||||||
return loader;
|
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.
|
* Renames an existing chunk loader.
|
||||||
*/
|
*/
|
||||||
@@ -337,6 +368,10 @@ final class ChunkLoaders {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (ChunkLoader loader : currentLoaders) {
|
for (ChunkLoader loader : currentLoaders) {
|
||||||
|
if (loader.isExpired() && loader.enabled()) {
|
||||||
|
setEnabled(loader.id(), false);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!loader.enabled()) {
|
if (!loader.enabled()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -480,6 +515,7 @@ final class ChunkLoaders {
|
|||||||
String key = "loaders." + l.id();
|
String key = "loaders." + l.id();
|
||||||
yaml.set(key + ".name", l.name());
|
yaml.set(key + ".name", l.name());
|
||||||
yaml.set(key + ".enabled", l.enabled());
|
yaml.set(key + ".enabled", l.enabled());
|
||||||
|
yaml.set(key + ".expires_at", l.expiresAt());
|
||||||
yaml.set(key + ".owner_uuid", l.ownerUuid());
|
yaml.set(key + ".owner_uuid", l.ownerUuid());
|
||||||
yaml.set(key + ".owner_name", l.ownerName());
|
yaml.set(key + ".owner_name", l.ownerName());
|
||||||
yaml.set(key + ".world", l.world());
|
yaml.set(key + ".world", l.world());
|
||||||
|
|||||||
@@ -135,6 +135,24 @@ class ChunkLoaderTest {
|
|||||||
assertFalse(reloadedLoader.enabled());
|
assertFalse(reloadedLoader.enabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void managesExpirationAndFuelTime() {
|
||||||
|
ChunkLoader loader = loaders.add("uuid-marcos", "Marcos", "world", 100, 64, 200, "Base", 0L);
|
||||||
|
assertEquals("Permanente", loader.timeLeft());
|
||||||
|
assertFalse(loader.isExpired());
|
||||||
|
|
||||||
|
assertTrue(loaders.addTime(loader.id(), 2 * 3600_000L)); // +2h
|
||||||
|
ChunkLoader timed = loaders.byId(loader.id());
|
||||||
|
assertNotNull(timed);
|
||||||
|
assertTrue(timed.expiresAt() > System.currentTimeMillis());
|
||||||
|
assertFalse(timed.isExpired());
|
||||||
|
assertTrue(timed.timeLeft().contains("h"));
|
||||||
|
|
||||||
|
ChunkLoader expired = timed.withExpiresAt(System.currentTimeMillis() - 1000L);
|
||||||
|
assertTrue(expired.isExpired());
|
||||||
|
assertEquals("Expirado", expired.timeLeft());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void simulationSafelyNoopsWithNullPlugin() {
|
void simulationSafelyNoopsWithNullPlugin() {
|
||||||
loaders.startSimulation();
|
loaders.startSimulation();
|
||||||
|
|||||||
Reference in New Issue
Block a user