refactor: reset plugin and integrate WorldGuard/WorldEdit dependencies

This commit is contained in:
2026-08-19 21:25:01 +02:00
parent 6a3277f8b0
commit d9d556229a
16 changed files with 24 additions and 1649 deletions
+6 -1
View File
@@ -11,13 +11,18 @@ repositories {
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
maven("https://oss.sonatype.org/content/groups/public/")
maven("https://maven.enginehub.org/repo/")
maven("https://jitpack.io")
}
dependencies {
// Paper API (1.20.4 gives access to modern Paper API while remaining cross-compatible)
// Paper API (1.20.4)
compileOnly("io.papermc.paper:paper-api:1.20.4-R0.1-SNAPSHOT")
// WorldGuard & WorldEdit APIs
compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.0.9")
compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.3.0")
// Adventure platform for Bukkit (cross-version RGB, MiniMessage, Titles, ActionBars)
implementation("net.kyori:adventure-platform-bukkit:4.3.4")
implementation("net.kyori:adventure-text-minimessage:4.17.0")
-150
View File
@@ -1,150 +0,0 @@
@startuml mining_model
skinparam roundcorner 8
skinparam shadowing false
skinparam classAttributeIconSize 0
title Système de Minage Personnalisé (GamingCore) - Modèle & Architecture
package "Database Schema (SQLite / MySQL)" <<Rectangle>> {
class "ores" as OresTable << (T,#5DADE2) >> {
+ id : VARCHAR(64) [PK]
--
+ block_material : VARCHAR(64)
+ hardness : DOUBLE
+ rarity : VARCHAR(32)
+ stock : INTEGER
+ respawnable : BOOLEAN
+ respawn_time : INTEGER
+ temp_block : VARCHAR(64)
+ created_at : TIMESTAMP
}
class "ore_drops" as OreDropsTable << (T,#5DADE2) >> {
+ id : INTEGER [PK AUTO]
--
+ ore_id : VARCHAR(64) [FK]
+ item_material : VARCHAR(64)
+ default_amount : INTEGER
+ default_chance : DOUBLE
+ fortunable : BOOLEAN
+ fortune_multiplier : DOUBLE
+ smeltable : BOOLEAN
+ smelted_material : VARCHAR(64)
}
class "placed_ores" as PlacedOresTable << (T,#5DADE2) >> {
+ id : INTEGER [PK AUTO]
--
+ ore_id : VARCHAR(64) [FK]
+ world : VARCHAR(64)
+ x : INTEGER
+ y : INTEGER
+ z : INTEGER
+ current_stock : INTEGER
+ is_depleted : BOOLEAN
+ next_respawn_at : BIGINT
}
OresTable "1" *-- "0..*" OreDropsTable : "drops configurés"
OresTable "1" *-- "0..*" PlacedOresTable : "blocs actifs"
}
package "fr.luc.gamingcore.mining.model" {
class CustomOre {
- id: String
- blockMaterial: XMaterial
- hardness: double
- rarity: OreRarity
- stock: int
- respawnable: boolean
- respawnTimeSeconds: int
- tempBlockMaterial: XMaterial
- drops: List<OreDrop>
+ isDepleted(currentStock: int): boolean
}
class OreDrop {
- id: int
- itemMaterial: XMaterial
- defaultAmount: int
- defaultChance: double
- fortunable: boolean
- fortuneMultiplier: double
- smeltable: boolean
- smeltedMaterial: XMaterial
+ calculateDrop(fortuneLevel: int, hasSmeltEnchant: boolean): ItemStack
}
class PlacedOreBlock {
- id: int
- oreType: CustomOre
- worldName: String
- x: int
- y: int
- z: int
- currentStock: int
- depleted: boolean
- nextRespawnTimestamp: long
+ mine(player: Player): List<ItemStack>
+ triggerRespawn(): void
}
enum OreRarity {
COMMON
UNCOMMON
RARE
EPIC
LEGENDARY
MYTHIC
}
}
package "fr.luc.gamingcore.mining.manager" {
class OreManager {
- ores: Map<String, CustomOre>
- placedBlocks: Map<BlockLocation, PlacedOreBlock>
+ registerOre(ore: CustomOre): void
+ getOre(id: String): CustomOre
+ getPlacedBlock(location: Location): PlacedOreBlock
+ placeOreBlock(oreId: String, location: Location): PlacedOreBlock
+ removeOreBlock(location: Location): void
}
class RespawnScheduler {
+ startTask(): void
+ checkRespawnQueue(): void
}
class DatabaseManager {
- connectionSource: Connection
+ initTables(): void
+ loadAllOres(): List<CustomOre>
+ loadPlacedBlocks(): List<PlacedOreBlock>
+ savePlacedBlock(block: PlacedOreBlock): void
+ updatePlacedBlockState(block: PlacedOreBlock): void
+ deletePlacedBlock(id: int): void
}
}
package "fr.luc.gamingcore.mining.listener" {
class BlockBreakListener {
+ onBlockBreak(event: BlockBreakEvent): void
}
class BlockPlaceListener {
+ onBlockPlace(event: BlockPlaceEvent): void
}
}
CustomOre "1" *-- "0..*" OreDrop
CustomOre "1" -- "0..*" PlacedOreBlock
CustomOre --> OreRarity
OreManager o-- CustomOre
OreManager o-- PlacedOreBlock
OreManager --> DatabaseManager
OreManager --> RespawnScheduler
BlockBreakListener --> OreManager
BlockPlaceListener --> OreManager
@enduml
+13 -42
View File
@@ -1,21 +1,14 @@
package fr.luc.gamingcore;
import fr.luc.gamingcore.mining.command.MiningCommand;
import fr.luc.gamingcore.mining.database.DatabaseManager;
import fr.luc.gamingcore.mining.listener.BlockBreakListener;
import fr.luc.gamingcore.mining.listener.BlockProtectionListener;
import fr.luc.gamingcore.mining.manager.OreManager;
import com.sk89q.worldguard.WorldGuard;
import com.sk89q.worldguard.protection.regions.RegionContainer;
import fr.luc.gamingcore.util.TextUtil;
import org.bukkit.command.PluginCommand;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
public final class GamingCore extends JavaPlugin {
private static GamingCore instance;
private DatabaseManager databaseManager;
private OreManager oreManager;
private RegionContainer regionContainer;
@Override
public void onEnable() {
@@ -23,38 +16,21 @@ public final class GamingCore extends JavaPlugin {
saveDefaultConfig();
// Initialize Text Util prefix from config
TextUtil.setPrefix(getConfig().getString("prefix"));
TextUtil.setPrefix(getConfig().getString("prefix", "<gradient:#00C9FF:#92FE9D>[GamingCore]</gradient> <dark_gray>»</dark_gray> <gray>"));
// Initialize SQLite Database
this.databaseManager = new DatabaseManager(this);
this.databaseManager.initialize();
// Initialize Ore Manager & Load Data
this.oreManager = new OreManager(this, databaseManager);
this.oreManager.loadData();
// Register Listeners
PluginManager pm = getServer().getPluginManager();
pm.registerEvents(new BlockBreakListener(this, oreManager), this);
pm.registerEvents(new BlockProtectionListener(oreManager), this);
// Register Commands
MiningCommand miningCommand = new MiningCommand(this, oreManager);
PluginCommand gcCmd = getCommand("gamingcore");
if (gcCmd != null) {
gcCmd.setExecutor(miningCommand);
gcCmd.setTabCompleter(miningCommand);
// Hook into WorldGuard
try {
this.regionContainer = WorldGuard.getInstance().getPlatform().getRegionContainer();
getLogger().info("Connexion avec WorldGuard 7 etablie avec succes !");
} catch (Throwable t) {
getLogger().severe("Impossible de se connecter a WorldGuard : " + t.getMessage());
}
getLogger().info("GamingCore (Custom Mining Module) a ete active avec succes !");
getLogger().info("GamingCore a ete active avec succes !");
}
@Override
public void onDisable() {
if (oreManager != null) {
oreManager.shutdown();
}
getLogger().info("GamingCore a ete desactive !");
}
@@ -62,12 +38,7 @@ public final class GamingCore extends JavaPlugin {
return instance;
}
public DatabaseManager getDatabaseManager() {
return databaseManager;
}
public OreManager getOreManager() {
return oreManager;
public RegionContainer getRegionContainer() {
return regionContainer;
}
}
@@ -1,242 +0,0 @@
package fr.luc.gamingcore.mining.command;
import fr.luc.gamingcore.GamingCore;
import fr.luc.gamingcore.mining.manager.OreManager;
import fr.luc.gamingcore.mining.model.CustomOre;
import fr.luc.gamingcore.mining.model.OreDrop;
import fr.luc.gamingcore.mining.model.PlacedOreBlock;
import fr.luc.gamingcore.util.TextUtil;
import org.bukkit.Location;
import org.bukkit.block.Block;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabCompleter;
import org.bukkit.entity.Player;
import java.util.*;
import java.util.stream.Collectors;
public class MiningCommand implements CommandExecutor, TabCompleter {
private final GamingCore plugin;
private final OreManager oreManager;
public MiningCommand(GamingCore plugin, OreManager oreManager) {
this.plugin = plugin;
this.oreManager = oreManager;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 0 || args[0].equalsIgnoreCase("help")) {
sendHelp(sender, label);
return true;
}
String sub = args[0].toLowerCase();
if (sub.equals("reload")) {
if (!sender.hasPermission("gamingcore.admin")) {
TextUtil.sendMessage(sender, "<red>Tu n'as pas la permission d'exécuter cette commande.</red>");
return true;
}
plugin.reloadConfig();
TextUtil.setPrefix(plugin.getConfig().getString("prefix"));
oreManager.loadData();
TextUtil.sendMessage(sender, "<green>Configuration et minerais rechargés avec succès !</green>");
return true;
}
if (sub.equals("ore")) {
if (args.length < 2) {
sendHelp(sender, label);
return true;
}
String action = args[1].toLowerCase();
switch (action) {
case "list":
handleList(sender);
return true;
case "set":
case "place":
if (!sender.hasPermission("gamingcore.admin")) {
TextUtil.sendMessage(sender, "<red>Tu n'as pas la permission d'exécuter cette commande.</red>");
return true;
}
if (!(sender instanceof Player)) {
sender.sendMessage("Cette commande doit être exécutée par un joueur.");
return true;
}
if (args.length < 3) {
TextUtil.sendMessage(sender, "<red>Utilisation: /" + label + " ore set <ore_id></red>");
return true;
}
handleSetOre((Player) sender, args[2]);
return true;
case "remove":
case "delete":
if (!sender.hasPermission("gamingcore.admin")) {
TextUtil.sendMessage(sender, "<red>Tu n'as pas la permission d'exécuter cette commande.</red>");
return true;
}
if (!(sender instanceof Player)) {
sender.sendMessage("Cette commande doit être exécutée par un joueur.");
return true;
}
handleRemoveOre((Player) sender);
return true;
case "info":
if (!(sender instanceof Player)) {
sender.sendMessage("Cette commande doit être exécutée par un joueur.");
return true;
}
handleInfoOre((Player) sender);
return true;
default:
sendHelp(sender, label);
return true;
}
}
sendHelp(sender, label);
return true;
}
private void handleList(CommandSender sender) {
Collection<CustomOre> ores = oreManager.getAllOres();
if (ores.isEmpty()) {
TextUtil.sendMessage(sender, "<yellow>Aucun minerai personnalisé n'est actuellement configuré.</yellow>");
return;
}
TextUtil.sendRawMessage(sender, "<gradient:#00C9FF:#92FE9D>═══════════ [ Minerais Personnalisés ] ═══════════</gradient>");
for (CustomOre ore : ores) {
TextUtil.sendRawMessage(sender, " <dark_gray>•</dark_gray> <white><bold>" + ore.getId() + "</bold></white> - " + ore.getDisplayName() +
" " + ore.getRarity().getTag() + " <gray>(Stock: <yellow>" + ore.getStock() + "</yellow>, Respawn: <yellow>" + ore.getRespawnTimeSeconds() + "s</yellow>)</gray>");
for (OreDrop drop : ore.getDrops()) {
TextUtil.sendRawMessage(sender, " <dark_gray>└─</dark_gray> <gray>Drop: <white>" + drop.getItemMaterial().name() + "</white> x" + drop.getDefaultAmount() +
" <dark_gray>(" + (int)(drop.getDefaultChance() * 100) + "%)</dark_gray></gray>");
}
}
TextUtil.sendRawMessage(sender, "<gradient:#00C9FF:#92FE9D>══════════════════════════════════════════════════</gradient>");
}
private void handleSetOre(Player player, String oreId) {
CustomOre ore = oreManager.getOre(oreId);
if (ore == null) {
TextUtil.sendMessage(player, "<red>Minerai introuvable : <yellow>" + oreId + "</yellow>. Tape /gc ore list pour voir la liste.</red>");
return;
}
Block targetBlock = player.getTargetBlockExact(5);
if (targetBlock == null || targetBlock.isEmpty()) {
TextUtil.sendMessage(player, "<red>Tu dois regarder un bloc valide à moins de 5 blocs de distance.</red>");
return;
}
Location loc = targetBlock.getLocation();
PlacedOreBlock placed = oreManager.placeOreBlock(ore.getId(), loc);
if (placed != null) {
TextUtil.sendMessage(player, "<green>Bloc défini avec succès en tant que minerai <white>" + ore.getId() + "</white> (" + ore.getDisplayName() + ") !</green>");
} else {
TextUtil.sendMessage(player, "<red>Une erreur est survenue lors de la pose du minerai.</red>");
}
}
private void handleRemoveOre(Player player) {
Block targetBlock = player.getTargetBlockExact(5);
if (targetBlock == null || targetBlock.isEmpty()) {
TextUtil.sendMessage(player, "<red>Tu dois regarder un bloc valide à moins de 5 blocs de distance.</red>");
return;
}
boolean removed = oreManager.removeOreBlock(targetBlock.getLocation());
if (removed) {
TextUtil.sendMessage(player, "<green>Minerai personnalisé retiré avec succès de ce bloc.</green>");
} else {
TextUtil.sendMessage(player, "<red>Ce bloc n'est pas un minerai personnalisé actif.</red>");
}
}
private void handleInfoOre(Player player) {
Block targetBlock = player.getTargetBlockExact(5);
if (targetBlock == null || targetBlock.isEmpty()) {
TextUtil.sendMessage(player, "<red>Tu dois regarder un bloc valide à moins de 5 blocs de distance.</red>");
return;
}
PlacedOreBlock placed = oreManager.getPlacedBlock(targetBlock.getLocation());
if (placed == null) {
TextUtil.sendMessage(player, "<red>Ce bloc n'est pas un minerai personnalisé.</red>");
return;
}
CustomOre ore = placed.getOreType();
TextUtil.sendRawMessage(player, "<gradient:#00C9FF:#92FE9D>═══════════ [ Info Minerai ] ═══════════</gradient>");
TextUtil.sendRawMessage(player, " <gray>ID : <white>" + placed.getOreId() + "</white></gray>");
TextUtil.sendRawMessage(player, " <gray>Nom : " + ore.getDisplayName() + "</gray>");
TextUtil.sendRawMessage(player, " <gray>Rareté : " + ore.getRarity().getTag() + "</gray>");
TextUtil.sendRawMessage(player, " <gray>Stock : <yellow>" + placed.getCurrentStock() + "</yellow>/<white>" + ore.getStock() + "</white></gray>");
TextUtil.sendRawMessage(player, " <gray>Épuisé : <white>" + (placed.isDepleted() ? "<red>Oui</red>" : "<green>Non</green>") + "</white></gray>");
if (placed.isDepleted()) {
long remaining = Math.max(0, (placed.getNextRespawnTimestamp() - System.currentTimeMillis()) / 1000);
TextUtil.sendRawMessage(player, " <gray>Respawn dans : <yellow>" + remaining + "s</yellow></gray>");
}
TextUtil.sendRawMessage(player, "<gradient:#00C9FF:#92FE9D>════════════════════════════════════════</gradient>");
}
private void sendHelp(CommandSender sender, String label) {
TextUtil.sendRawMessage(sender, "<gradient:#00C9FF:#92FE9D>═══════════ [ GamingCore Mining ] ═══════════</gradient>");
TextUtil.sendRawMessage(sender, " <yellow>/" + label + " ore list</yellow> <dark_gray>-</dark_gray> <gray>Liste des minerais disponibles</gray>");
TextUtil.sendRawMessage(sender, " <yellow>/" + label + " ore info</yellow> <dark_gray>-</dark_gray> <gray>Voir les infos du bloc ciblé</gray>");
if (sender.hasPermission("gamingcore.admin")) {
TextUtil.sendRawMessage(sender, " <yellow>/" + label + " ore set <ore_id></yellow> <dark_gray>-</dark_gray> <gray>Transformer le bloc ciblé en minerai</gray>");
TextUtil.sendRawMessage(sender, " <yellow>/" + label + " ore remove</yellow> <dark_gray>-</dark_gray> <gray>Retirer le minerai ciblé</gray>");
TextUtil.sendRawMessage(sender, " <yellow>/" + label + " reload</yellow> <dark_gray>-</dark_gray> <gray>Recharger la configuration et la BDD</gray>");
}
TextUtil.sendRawMessage(sender, "<gradient:#00C9FF:#92FE9D>════════════════════════════════════════════</gradient>");
}
@Override
public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
List<String> completions = new ArrayList<>();
if (args.length == 1) {
List<String> subcommands = new ArrayList<>(Arrays.asList("help", "ore"));
if (sender.hasPermission("gamingcore.admin")) {
subcommands.add("reload");
}
return subcommands.stream()
.filter(s -> s.toLowerCase().startsWith(args[0].toLowerCase()))
.collect(Collectors.toList());
}
if (args.length == 2 && args[0].equalsIgnoreCase("ore")) {
List<String> oreSubcommands = new ArrayList<>(Arrays.asList("list", "info"));
if (sender.hasPermission("gamingcore.admin")) {
oreSubcommands.addAll(Arrays.asList("set", "remove"));
}
return oreSubcommands.stream()
.filter(s -> s.toLowerCase().startsWith(args[1].toLowerCase()))
.collect(Collectors.toList());
}
if (args.length == 3 && args[0].equalsIgnoreCase("ore") && args[1].equalsIgnoreCase("set")) {
if (sender.hasPermission("gamingcore.admin")) {
return oreManager.getAllOres().stream()
.map(CustomOre::getId)
.filter(id -> id.toLowerCase().startsWith(args[2].toLowerCase()))
.collect(Collectors.toList());
}
}
return completions;
}
}
@@ -1,328 +0,0 @@
package fr.luc.gamingcore.mining.database;
import com.cryptomorin.xseries.XMaterial;
import fr.luc.gamingcore.GamingCore;
import fr.luc.gamingcore.mining.model.*;
import org.bukkit.Bukkit;
import java.io.File;
import java.sql.*;
import java.util.*;
import java.util.logging.Level;
public class DatabaseManager {
private final GamingCore plugin;
private final File dbFile;
private Connection connection;
public DatabaseManager(GamingCore plugin) {
this.plugin = plugin;
this.dbFile = new File(plugin.getDataFolder(), "database.db");
}
public synchronized void initialize() {
if (!plugin.getDataFolder().exists()) {
plugin.getDataFolder().mkdirs();
}
try {
connect();
createTables();
seedDefaultOresIfEmpty();
} catch (SQLException e) {
plugin.getLogger().log(Level.SEVERE, "Impossible d'initialiser la base de donnees SQLite !", e);
}
}
private synchronized void connect() throws SQLException {
if (connection != null && !connection.isClosed()) {
return;
}
try {
Class.forName("org.sqlite.JDBC");
} catch (ClassNotFoundException e) {
// Paper runtime includes SQLite JDBC by default
}
String url = "jdbc:sqlite:" + dbFile.getAbsolutePath();
connection = DriverManager.getConnection(url);
}
private synchronized Connection getConnection() throws SQLException {
if (connection == null || connection.isClosed()) {
connect();
}
return connection;
}
private void createTables() throws SQLException {
try (Statement stmt = getConnection().createStatement()) {
// Ores Table
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ores (" +
"id VARCHAR(64) PRIMARY KEY, " +
"display_name VARCHAR(64) NOT NULL, " +
"block_material VARCHAR(64) NOT NULL, " +
"hardness DOUBLE NOT NULL DEFAULT 1.0, " +
"rarity VARCHAR(32) NOT NULL DEFAULT 'COMMON', " +
"stock INTEGER NOT NULL DEFAULT 1, " +
"respawnable INTEGER NOT NULL DEFAULT 1, " +
"respawn_time INTEGER NOT NULL DEFAULT 10, " +
"temp_block VARCHAR(64) NOT NULL DEFAULT 'BEDROCK', " +
"created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP" +
");");
// Ore Drops Table
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ore_drops (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"ore_id VARCHAR(64) NOT NULL, " +
"item_material VARCHAR(64) NOT NULL, " +
"default_amount INTEGER NOT NULL DEFAULT 1, " +
"default_chance DOUBLE NOT NULL DEFAULT 1.0, " +
"fortunable INTEGER NOT NULL DEFAULT 1, " +
"fortune_multiplier DOUBLE NOT NULL DEFAULT 1.0, " +
"smeltable INTEGER NOT NULL DEFAULT 0, " +
"smelted_material VARCHAR(64), " +
"FOREIGN KEY (ore_id) REFERENCES ores(id) ON DELETE CASCADE" +
");");
// Placed Ores Table
stmt.executeUpdate("CREATE TABLE IF NOT EXISTS placed_ores (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"ore_id VARCHAR(64) NOT NULL, " +
"world VARCHAR(64) NOT NULL, " +
"x INTEGER NOT NULL, " +
"y INTEGER NOT NULL, " +
"z INTEGER NOT NULL, " +
"current_stock INTEGER NOT NULL, " +
"is_depleted INTEGER NOT NULL DEFAULT 0, " +
"next_respawn_at BIGINT NOT NULL DEFAULT 0, " +
"FOREIGN KEY (ore_id) REFERENCES ores(id) ON DELETE CASCADE" +
");");
}
}
private void seedDefaultOresIfEmpty() throws SQLException {
try (Statement stmt = getConnection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM ores")) {
if (rs.next() && rs.getInt(1) == 0) {
plugin.getLogger().info("Insertion des minerais par defaut dans la base SQLite...");
// 1. Custom Iron
CustomOre iron = new CustomOre("custom_iron", "<white>Fer Renforcé</white>", XMaterial.IRON_ORE, 3.0,
OreRarity.COMMON, 3, true, 15, XMaterial.BEDROCK, new ArrayList<>());
iron.addDrop(new OreDrop(0, "custom_iron", XMaterial.RAW_IRON, 1, 1.0, true, 1.0, true, XMaterial.IRON_INGOT));
saveOre(iron);
// 2. Custom Gold
CustomOre gold = new CustomOre("custom_gold", "<gold>Or Pur</gold>", XMaterial.GOLD_ORE, 3.5,
OreRarity.UNCOMMON, 4, true, 20, XMaterial.BEDROCK, new ArrayList<>());
gold.addDrop(new OreDrop(0, "custom_gold", XMaterial.RAW_GOLD, 1, 1.0, true, 1.0, true, XMaterial.GOLD_INGOT));
saveOre(gold);
// 3. Custom Diamond
CustomOre diamond = new CustomOre("custom_diamond", "<aqua>Diamant Cristallin</aqua>", XMaterial.DIAMOND_ORE, 5.0,
OreRarity.RARE, 5, true, 45, XMaterial.BEDROCK, new ArrayList<>());
diamond.addDrop(new OreDrop(0, "custom_diamond", XMaterial.DIAMOND, 1, 1.0, true, 1.2, false, null));
saveOre(diamond);
// 4. Custom Ancient Debris / Netherite
CustomOre netherite = new CustomOre("custom_netherite", "<dark_purple>Débris Ancien Sacré</dark_purple>", XMaterial.ANCIENT_DEBRIS, 8.0,
OreRarity.LEGENDARY, 8, true, 120, XMaterial.BEDROCK, new ArrayList<>());
netherite.addDrop(new OreDrop(0, "custom_netherite", XMaterial.NETHERITE_SCRAP, 1, 0.9, true, 1.3, false, null));
saveOre(netherite);
}
}
}
public synchronized void saveOre(CustomOre ore) throws SQLException {
String sql = "INSERT OR REPLACE INTO ores (id, display_name, block_material, hardness, rarity, stock, respawnable, respawn_time, temp_block) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
try (PreparedStatement ps = getConnection().prepareStatement(sql)) {
ps.setString(1, ore.getId());
ps.setString(2, ore.getDisplayName());
ps.setString(3, ore.getBlockMaterial().name());
ps.setDouble(4, ore.getHardness());
ps.setString(5, ore.getRarity().name());
ps.setInt(6, ore.getStock());
ps.setInt(7, ore.isRespawnable() ? 1 : 0);
ps.setInt(8, ore.getRespawnTimeSeconds());
ps.setString(9, ore.getTempBlockMaterial().name());
ps.executeUpdate();
}
// Drops
for (OreDrop drop : ore.getDrops()) {
saveOreDrop(drop);
}
}
public synchronized void saveOreDrop(OreDrop drop) throws SQLException {
String sql = "INSERT INTO ore_drops (ore_id, item_material, default_amount, default_chance, fortunable, fortune_multiplier, smeltable, smelted_material) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
try (PreparedStatement ps = getConnection().prepareStatement(sql)) {
ps.setString(1, drop.getOreId());
ps.setString(2, drop.getItemMaterial().name());
ps.setInt(3, drop.getDefaultAmount());
ps.setDouble(4, drop.getDefaultChance());
ps.setInt(5, drop.isFortunable() ? 1 : 0);
ps.setDouble(6, drop.getFortuneMultiplier());
ps.setInt(7, drop.isSmeltable() ? 1 : 0);
ps.setString(8, drop.getSmeltedMaterial() != null ? drop.getSmeltedMaterial().name() : null);
ps.executeUpdate();
}
}
public synchronized Map<String, CustomOre> loadAllOres() {
Map<String, CustomOre> ores = new HashMap<>();
String sql = "SELECT * FROM ores";
try (Statement stmt = getConnection().createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
String id = rs.getString("id");
String displayName = rs.getString("display_name");
XMaterial blockMat = XMaterial.matchXMaterial(rs.getString("block_material")).orElse(XMaterial.STONE);
double hardness = rs.getDouble("hardness");
OreRarity rarity = OreRarity.fromString(rs.getString("rarity"));
int stock = rs.getInt("stock");
boolean respawnable = rs.getInt("respawnable") == 1;
int respawnTime = rs.getInt("respawn_time");
XMaterial tempBlock = XMaterial.matchXMaterial(rs.getString("temp_block")).orElse(XMaterial.BEDROCK);
CustomOre ore = new CustomOre(id, displayName, blockMat, hardness, rarity, stock, respawnable, respawnTime, tempBlock, new ArrayList<>());
ores.put(id, ore);
}
} catch (SQLException e) {
plugin.getLogger().log(Level.SEVERE, "Erreur lors du chargement des minerais !", e);
}
// Load drops for all ores
String dropsSql = "SELECT * FROM ore_drops";
try (Statement stmt = getConnection().createStatement();
ResultSet rs = stmt.executeQuery(dropsSql)) {
while (rs.next()) {
int dropId = rs.getInt("id");
String oreId = rs.getString("ore_id");
XMaterial itemMat = XMaterial.matchXMaterial(rs.getString("item_material")).orElse(XMaterial.COBBLESTONE);
int amount = rs.getInt("default_amount");
double chance = rs.getDouble("default_chance");
boolean fortunable = rs.getInt("fortunable") == 1;
double fortuneMult = rs.getDouble("fortune_multiplier");
boolean smeltable = rs.getInt("smeltable") == 1;
String smeltedMatStr = rs.getString("smelted_material");
XMaterial smeltedMat = smeltedMatStr != null ? XMaterial.matchXMaterial(smeltedMatStr).orElse(null) : null;
CustomOre ore = ores.get(oreId);
if (ore != null) {
OreDrop drop = new OreDrop(dropId, oreId, itemMat, amount, chance, fortunable, fortuneMult, smeltable, smeltedMat);
ore.addDrop(drop);
}
}
} catch (SQLException e) {
plugin.getLogger().log(Level.SEVERE, "Erreur lors du chargement des drops de minerais !", e);
}
return ores;
}
public synchronized Map<BlockLocation, PlacedOreBlock> loadPlacedBlocks(Map<String, CustomOre> ores) {
Map<BlockLocation, PlacedOreBlock> map = new HashMap<>();
String sql = "SELECT * FROM placed_ores";
try (Statement stmt = getConnection().createStatement();
ResultSet rs = stmt.executeQuery(sql)) {
while (rs.next()) {
int id = rs.getInt("id");
String oreId = rs.getString("ore_id");
String world = rs.getString("world");
int x = rs.getInt("x");
int y = rs.getInt("y");
int z = rs.getInt("z");
int stock = rs.getInt("current_stock");
boolean depleted = rs.getInt("is_depleted") == 1;
long nextRespawnAt = rs.getLong("next_respawn_at");
CustomOre ore = ores.get(oreId);
if (ore != null) {
BlockLocation loc = new BlockLocation(world, x, y, z);
PlacedOreBlock placed = new PlacedOreBlock(id, oreId, ore, loc, stock, depleted, nextRespawnAt);
map.put(loc, placed);
}
}
} catch (SQLException e) {
plugin.getLogger().log(Level.SEVERE, "Erreur lors du chargement des blocs places !", e);
}
return map;
}
public synchronized int insertPlacedBlock(PlacedOreBlock block) {
String sql = "INSERT INTO placed_ores (ore_id, world, x, y, z, current_stock, is_depleted, next_respawn_at) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
try (PreparedStatement ps = getConnection().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
ps.setString(1, block.getOreId());
ps.setString(2, block.getLocation().getWorldName());
ps.setInt(3, block.getLocation().getX());
ps.setInt(4, block.getLocation().getY());
ps.setInt(5, block.getLocation().getZ());
ps.setInt(6, block.getCurrentStock());
ps.setInt(7, block.isDepleted() ? 1 : 0);
ps.setLong(8, block.getNextRespawnTimestamp());
ps.executeUpdate();
try (ResultSet rs = ps.getGeneratedKeys()) {
if (rs.next()) {
int generatedId = rs.getInt(1);
block.setId(generatedId);
return generatedId;
}
}
} catch (SQLException e) {
plugin.getLogger().log(Level.SEVERE, "Erreur lors de l'enregistrement d'un minerai pose !", e);
}
return -1;
}
public void updatePlacedBlockAsync(PlacedOreBlock block) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
String sql = "UPDATE placed_ores SET current_stock = ?, is_depleted = ?, next_respawn_at = ? WHERE id = ?";
try (PreparedStatement ps = getConnection().prepareStatement(sql)) {
ps.setInt(1, block.getCurrentStock());
ps.setInt(2, block.isDepleted() ? 1 : 0);
ps.setLong(3, block.getNextRespawnTimestamp());
ps.setInt(4, block.getId());
ps.executeUpdate();
} catch (SQLException e) {
plugin.getLogger().log(Level.SEVERE, "Erreur lors de la mise a jour de l'etat du minerai pose !", e);
}
});
}
public void deletePlacedBlockAsync(BlockLocation loc) {
Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> {
String sql = "DELETE FROM placed_ores WHERE world = ? AND x = ? AND y = ? AND z = ?";
try (PreparedStatement ps = getConnection().prepareStatement(sql)) {
ps.setString(1, loc.getWorldName());
ps.setInt(2, loc.getX());
ps.setInt(3, loc.getY());
ps.setInt(4, loc.getZ());
ps.executeUpdate();
} catch (SQLException e) {
plugin.getLogger().log(Level.SEVERE, "Erreur lors de la suppression d'un minerai pose !", e);
}
});
}
public synchronized void close() {
try {
if (connection != null && !connection.isClosed()) {
connection.close();
}
} catch (SQLException e) {
plugin.getLogger().log(Level.WARNING, "Erreur lors de la fermeture de la connexion SQLite", e);
}
}
}
@@ -1,46 +0,0 @@
package fr.luc.gamingcore.mining.listener;
import fr.luc.gamingcore.GamingCore;
import fr.luc.gamingcore.mining.manager.OreManager;
import fr.luc.gamingcore.mining.model.PlacedOreBlock;
import fr.luc.gamingcore.util.TextUtil;
import org.bukkit.GameMode;
import org.bukkit.block.Block;
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.BlockBreakEvent;
public class BlockBreakListener implements Listener {
private final GamingCore plugin;
private final OreManager oreManager;
public BlockBreakListener(GamingCore plugin, OreManager oreManager) {
this.plugin = plugin;
this.oreManager = oreManager;
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onBlockBreak(BlockBreakEvent event) {
Block block = event.getBlock();
PlacedOreBlock placed = oreManager.getPlacedBlock(block.getLocation());
if (placed == null) {
return;
}
Player player = event.getPlayer();
// If admin in creative mode sneaking breaks the block, remove it permanently
if (player.getGameMode() == GameMode.CREATIVE && player.isSneaking() && player.hasPermission("gamingcore.admin")) {
oreManager.removeOreBlock(block.getLocation());
TextUtil.sendMessage(player, "<green>Minerai personnalisé retiré avec succès !</green>");
event.setCancelled(false);
return;
}
// Delegate to OreManager for custom mining mechanics
oreManager.handleMine(player, event);
}
}
@@ -1,50 +0,0 @@
package fr.luc.gamingcore.mining.listener;
import fr.luc.gamingcore.mining.manager.OreManager;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.BlockExplodeEvent;
import org.bukkit.event.block.BlockPistonExtendEvent;
import org.bukkit.event.block.BlockPistonRetractEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
public class BlockProtectionListener implements Listener {
private final OreManager oreManager;
public BlockProtectionListener(OreManager oreManager) {
this.oreManager = oreManager;
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onEntityExplode(EntityExplodeEvent event) {
event.blockList().removeIf(block -> oreManager.isPlacedOre(block.getLocation()));
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onBlockExplode(BlockExplodeEvent event) {
event.blockList().removeIf(block -> oreManager.isPlacedOre(block.getLocation()));
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onPistonExtend(BlockPistonExtendEvent event) {
for (Block block : event.getBlocks()) {
if (oreManager.isPlacedOre(block.getLocation())) {
event.setCancelled(true);
return;
}
}
}
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
public void onPistonRetract(BlockPistonRetractEvent event) {
for (Block block : event.getBlocks()) {
if (oreManager.isPlacedOre(block.getLocation())) {
event.setCancelled(true);
return;
}
}
}
}
@@ -1,210 +0,0 @@
package fr.luc.gamingcore.mining.manager;
import com.cryptomorin.xseries.XMaterial;
import com.cryptomorin.xseries.XSound;
import fr.luc.gamingcore.GamingCore;
import fr.luc.gamingcore.mining.database.DatabaseManager;
import fr.luc.gamingcore.mining.model.*;
import fr.luc.gamingcore.util.TextUtil;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.block.Block;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.block.BlockBreakEvent;
import org.bukkit.inventory.ItemStack;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class OreManager {
private final GamingCore plugin;
private final DatabaseManager databaseManager;
private final RespawnScheduler respawnScheduler;
private final Map<String, CustomOre> registeredOres = new ConcurrentHashMap<>();
private final Map<BlockLocation, PlacedOreBlock> placedBlocks = new ConcurrentHashMap<>();
public OreManager(GamingCore plugin, DatabaseManager databaseManager) {
this.plugin = plugin;
this.databaseManager = databaseManager;
this.respawnScheduler = new RespawnScheduler(plugin, this);
}
public void loadData() {
registeredOres.clear();
placedBlocks.clear();
// 1. Load Custom Ores from DB
Map<String, CustomOre> ores = databaseManager.loadAllOres();
registeredOres.putAll(ores);
plugin.getLogger().info("Charges " + registeredOres.size() + " minerais personnalises.");
// 2. Load Placed Blocks from DB
Map<BlockLocation, PlacedOreBlock> placed = databaseManager.loadPlacedBlocks(registeredOres);
placedBlocks.putAll(placed);
plugin.getLogger().info("Charges " + placedBlocks.size() + " blocs de minerais actifs.");
// 3. Start Respawn Scheduler
respawnScheduler.start();
}
public void shutdown() {
respawnScheduler.stop();
databaseManager.close();
}
public DatabaseManager getDatabaseManager() {
return databaseManager;
}
public RespawnScheduler getRespawnScheduler() {
return respawnScheduler;
}
public CustomOre getOre(String id) {
if (id == null) return null;
return registeredOres.get(id.toLowerCase());
}
public Collection<CustomOre> getAllOres() {
return Collections.unmodifiableCollection(registeredOres.values());
}
public void registerOre(CustomOre ore) {
if (ore != null) {
registeredOres.put(ore.getId().toLowerCase(), ore);
try {
databaseManager.saveOre(ore);
} catch (Exception e) {
plugin.getLogger().warning("Erreur lors de la sauvegarde du minerai : " + ore.getId());
}
}
}
public PlacedOreBlock getPlacedBlock(Location loc) {
if (loc == null || loc.getWorld() == null) return null;
return placedBlocks.get(BlockLocation.fromLocation(loc));
}
public PlacedOreBlock getPlacedBlock(BlockLocation loc) {
if (loc == null) return null;
return placedBlocks.get(loc);
}
public Collection<PlacedOreBlock> getAllPlacedBlocks() {
return Collections.unmodifiableCollection(placedBlocks.values());
}
public boolean isPlacedOre(Location loc) {
return getPlacedBlock(loc) != null;
}
public PlacedOreBlock placeOreBlock(String oreId, Location loc) {
CustomOre ore = getOre(oreId);
if (ore == null || loc == null || loc.getWorld() == null) {
return null;
}
BlockLocation blockLoc = BlockLocation.fromLocation(loc);
PlacedOreBlock placed = new PlacedOreBlock(-1, ore.getId(), ore, blockLoc, ore.getStock(), false, 0);
// Update block in world
Block block = loc.getBlock();
block.setType(ore.parseBlockMaterial());
// Save in DB and cache
int generatedId = databaseManager.insertPlacedBlock(placed);
placed.setId(generatedId);
placedBlocks.put(blockLoc, placed);
return placed;
}
public boolean removeOreBlock(Location loc) {
if (loc == null || loc.getWorld() == null) return false;
BlockLocation blockLoc = BlockLocation.fromLocation(loc);
PlacedOreBlock removed = placedBlocks.remove(blockLoc);
if (removed != null) {
databaseManager.deletePlacedBlockAsync(blockLoc);
loc.getBlock().setType(Material.AIR);
return true;
}
return false;
}
/**
* Handles custom mining interaction when a player breaks a placed ore block.
*/
public void handleMine(Player player, BlockBreakEvent event) {
Block block = event.getBlock();
PlacedOreBlock placed = getPlacedBlock(block.getLocation());
if (placed == null) {
return;
}
// Cancel the standard vanilla break event
event.setCancelled(true);
// If block is already depleted and waiting to respawn, do nothing or notify player
if (placed.isDepleted()) {
long remainingSeconds = Math.max(1, (placed.getNextRespawnTimestamp() - System.currentTimeMillis()) / 1000);
TextUtil.sendActionBar(player, "<red>Ce minerai est épuisé ! Réapparition dans <yellow>" + remainingSeconds + "s</yellow>.</red>");
XSound.BLOCK_STONE_HIT.play(player, 0.7f, 0.5f);
return;
}
CustomOre ore = placed.getOreType();
if (ore == null) return;
// Tool & Enchantment checks
ItemStack handItem = player.getInventory().getItemInMainHand();
int fortuneLevel = 0;
if (handItem.hasItemMeta() && handItem.getItemMeta().hasEnchant(Enchantment.LOOT_BONUS_BLOCKS)) {
fortuneLevel = handItem.getEnchantmentLevel(Enchantment.LOOT_BONUS_BLOCKS);
}
// Calculate and drop items
Location dropLoc = block.getLocation().clone().add(0.5, 0.5, 0.5);
for (OreDrop drop : ore.getDrops()) {
ItemStack dropItem = drop.calculateDrop(fortuneLevel, false);
if (dropItem != null && dropItem.getType() != Material.AIR) {
block.getWorld().dropItemNaturally(dropLoc, dropItem);
}
}
// Visual & audio feedback for hit
try {
block.getWorld().spawnParticle(Particle.CRIT, dropLoc, 8, 0.2, 0.2, 0.2, 0.05);
} catch (Throwable ignored) {}
XSound.BLOCK_STONE_BREAK.play(player.getLocation(), 0.8f, 1.2f);
XSound.ENTITY_EXPERIENCE_ORB_PICKUP.play(player.getLocation(), 0.5f, 1.5f);
// Decrement stock
boolean justDepleted = placed.decrementStock();
if (justDepleted) {
// Transform block to tempBlock (e.g. Bedrock)
block.setType(ore.parseTempBlockMaterial());
// Visual effects for depletion
try {
block.getWorld().spawnParticle(Particle.SMOKE_LARGE, dropLoc, 10, 0.2, 0.2, 0.2, 0.02);
} catch (Throwable ignored) {}
XSound.BLOCK_ANVIL_LAND.play(player.getLocation(), 0.6f, 0.8f);
TextUtil.sendActionBar(player, "<red>⛏ " + ore.getDisplayName() + " <dark_gray>| <red>Épuisé ! (<yellow>" + ore.getRespawnTimeSeconds() + "s</yellow>)</red>");
} else {
// Stock remaining feedback
int current = placed.getCurrentStock();
int total = ore.getStock();
String stockColor = current > (total / 2) ? "<green>" : "<gold>";
TextUtil.sendActionBar(player, "<gray>⛏ " + ore.getDisplayName() + " <dark_gray>| <gray>Stock: " + stockColor + current + "</color><dark_gray>/</dark_gray><white>" + total + "</white>");
}
// Update database state asynchronously
databaseManager.updatePlacedBlockAsync(placed);
}
}
@@ -1,83 +0,0 @@
package fr.luc.gamingcore.mining.manager;
import com.cryptomorin.xseries.XSound;
import fr.luc.gamingcore.GamingCore;
import fr.luc.gamingcore.mining.model.BlockLocation;
import fr.luc.gamingcore.mining.model.PlacedOreBlock;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.block.Block;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitTask;
public class RespawnScheduler {
private final GamingCore plugin;
private final OreManager oreManager;
private BukkitTask task;
public RespawnScheduler(GamingCore plugin, OreManager oreManager) {
this.plugin = plugin;
this.oreManager = oreManager;
}
public void start() {
stop();
// Check every second (20 ticks)
this.task = new BukkitRunnable() {
@Override
public void run() {
checkRespawnQueue();
}
}.runTaskTimer(plugin, 20L, 20L);
}
public void stop() {
if (task != null && !task.isCancelled()) {
task.cancel();
task = null;
}
}
public void checkRespawnQueue() {
long now = System.currentTimeMillis();
for (PlacedOreBlock placed : oreManager.getAllPlacedBlocks()) {
if (!placed.isDepleted() || placed.getNextRespawnTimestamp() <= 0) {
continue;
}
if (now >= placed.getNextRespawnTimestamp()) {
respawnBlock(placed);
}
}
}
private void respawnBlock(PlacedOreBlock placed) {
BlockLocation blockLoc = placed.getLocation();
Location loc = blockLoc.toLocation();
if (loc == null || loc.getWorld() == null || !loc.getWorld().isChunkLoaded(loc.getBlockX() >> 4, loc.getBlockZ() >> 4)) {
// World not loaded or chunk not loaded: reset state anyway so it appears correct when chunk loads
placed.respawn();
oreManager.getDatabaseManager().updatePlacedBlockAsync(placed);
return;
}
Block block = loc.getBlock();
Material oreMat = placed.getOreType().parseBlockMaterial();
block.setType(oreMat);
// Respawn state in memory & database
placed.respawn();
oreManager.getDatabaseManager().updatePlacedBlockAsync(placed);
// Visual & audio effects
try {
loc.getWorld().spawnParticle(Particle.VILLAGER_HAPPY, loc.clone().add(0.5, 0.5, 0.5), 15, 0.3, 0.3, 0.3, 0.1);
} catch (Throwable ignored) {}
XSound.BLOCK_STONE_PLACE.play(loc, 1.0f, 1.2f);
}
}
@@ -1,82 +0,0 @@
package fr.luc.gamingcore.mining.model;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.block.Block;
import java.util.Objects;
public final class BlockLocation {
private final String worldName;
private final int x;
private final int y;
private final int z;
public BlockLocation(String worldName, int x, int y, int z) {
this.worldName = worldName != null ? worldName : "world";
this.x = x;
this.y = y;
this.z = z;
}
public static BlockLocation fromLocation(Location loc) {
if (loc == null || loc.getWorld() == null) {
throw new IllegalArgumentException("Location and World cannot be null");
}
return new BlockLocation(loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ());
}
public static BlockLocation fromBlock(Block block) {
if (block == null) {
throw new IllegalArgumentException("Block cannot be null");
}
return fromLocation(block.getLocation());
}
public Location toLocation() {
World world = Bukkit.getWorld(worldName);
if (world == null) return null;
return new Location(world, x, y, z);
}
public Block getBlock() {
Location loc = toLocation();
return loc != null ? loc.getBlock() : null;
}
public String getWorldName() {
return worldName;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getZ() {
return z;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BlockLocation that = (BlockLocation) o;
return x == that.x && y == that.y && z == that.z && Objects.equals(worldName, that.worldName);
}
@Override
public int hashCode() {
return Objects.hash(worldName, x, y, z);
}
@Override
public String toString() {
return worldName + ":" + x + "," + y + "," + z;
}
}
@@ -1,97 +0,0 @@
package fr.luc.gamingcore.mining.model;
import com.cryptomorin.xseries.XMaterial;
import org.bukkit.Material;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CustomOre {
private final String id;
private final String displayName;
private final XMaterial blockMaterial;
private final double hardness;
private final OreRarity rarity;
private final int stock;
private final boolean respawnable;
private final int respawnTimeSeconds;
private final XMaterial tempBlockMaterial;
private final List<OreDrop> drops;
public CustomOre(String id, String displayName, XMaterial blockMaterial, double hardness,
OreRarity rarity, int stock, boolean respawnable, int respawnTimeSeconds,
XMaterial tempBlockMaterial, List<OreDrop> drops) {
this.id = id;
this.displayName = displayName != null ? displayName : id;
this.blockMaterial = blockMaterial != null ? blockMaterial : XMaterial.STONE;
this.hardness = Math.max(0.1, hardness);
this.rarity = rarity != null ? rarity : OreRarity.COMMON;
this.stock = Math.max(1, stock);
this.respawnable = respawnable;
this.respawnTimeSeconds = Math.max(1, respawnTimeSeconds);
this.tempBlockMaterial = tempBlockMaterial != null ? tempBlockMaterial : XMaterial.BEDROCK;
this.drops = drops != null ? new ArrayList<>(drops) : new ArrayList<>();
}
public String getId() {
return id;
}
public String getDisplayName() {
return displayName;
}
public XMaterial getBlockMaterial() {
return blockMaterial;
}
public Material parseBlockMaterial() {
Material mat = blockMaterial.parseMaterial();
return mat != null ? mat : Material.STONE;
}
public double getHardness() {
return hardness;
}
public OreRarity getRarity() {
return rarity;
}
public int getStock() {
return stock;
}
public boolean isRespawnable() {
return respawnable;
}
public int getRespawnTimeSeconds() {
return respawnTimeSeconds;
}
public XMaterial getTempBlockMaterial() {
return tempBlockMaterial;
}
public Material parseTempBlockMaterial() {
Material mat = tempBlockMaterial.parseMaterial();
return mat != null ? mat : Material.BEDROCK;
}
public List<OreDrop> getDrops() {
return Collections.unmodifiableList(drops);
}
public void addDrop(OreDrop drop) {
if (drop != null) {
this.drops.add(drop);
}
}
public void clearDrops() {
this.drops.clear();
}
}
@@ -1,104 +0,0 @@
package fr.luc.gamingcore.mining.model;
import com.cryptomorin.xseries.XMaterial;
import org.bukkit.inventory.ItemStack;
import java.util.concurrent.ThreadLocalRandom;
public class OreDrop {
private final int id;
private final String oreId;
private final XMaterial itemMaterial;
private final int defaultAmount;
private final double defaultChance;
private final boolean fortunable;
private final double fortuneMultiplier;
private final boolean smeltable;
private final XMaterial smeltedMaterial;
public OreDrop(int id, String oreId, XMaterial itemMaterial, int defaultAmount, double defaultChance,
boolean fortunable, double fortuneMultiplier, boolean smeltable, XMaterial smeltedMaterial) {
this.id = id;
this.oreId = oreId;
this.itemMaterial = itemMaterial != null ? itemMaterial : XMaterial.COBBLESTONE;
this.defaultAmount = Math.max(1, defaultAmount);
this.defaultChance = Math.min(1.0, Math.max(0.0, defaultChance));
this.fortunable = fortunable;
this.fortuneMultiplier = fortuneMultiplier;
this.smeltable = smeltable;
this.smeltedMaterial = smeltedMaterial;
}
public int getId() {
return id;
}
public String getOreId() {
return oreId;
}
public XMaterial getItemMaterial() {
return itemMaterial;
}
public int getDefaultAmount() {
return defaultAmount;
}
public double getDefaultChance() {
return defaultChance;
}
public boolean isFortunable() {
return fortunable;
}
public double getFortuneMultiplier() {
return fortuneMultiplier;
}
public boolean isSmeltable() {
return smeltable;
}
public XMaterial getSmeltedMaterial() {
return smeltedMaterial;
}
/**
* Calculates the drop based on chance, fortune level, and auto-smelt status.
*
* @param fortuneLevel Level of Fortune enchantment on tool (0 if none)
* @param autoSmelt Whether auto-smelt is active
* @return ItemStack if drop was successful, or null if chance roll failed
*/
public ItemStack calculateDrop(int fortuneLevel, boolean autoSmelt) {
double roll = ThreadLocalRandom.current().nextDouble();
if (roll > defaultChance) {
return null; // Failed drop chance roll
}
int amount = defaultAmount;
if (fortunable && fortuneLevel > 0) {
// Vanilla-like fortune bonus calculation with customizable multiplier
int bonus = ThreadLocalRandom.current().nextInt(fortuneLevel + 2) - 1;
if (bonus > 0) {
amount += (int) Math.round(bonus * Math.max(1.0, fortuneMultiplier));
}
}
XMaterial targetMaterial = itemMaterial;
if (smeltable && autoSmelt && smeltedMaterial != null && smeltedMaterial.isSupported()) {
targetMaterial = smeltedMaterial;
}
ItemStack item = targetMaterial.parseItem();
if (item == null) {
return null;
}
item.setAmount(Math.max(1, amount));
return item;
}
}
@@ -1,41 +0,0 @@
package fr.luc.gamingcore.mining.model;
public enum OreRarity {
COMMON("Commun", "<gray>Commun</gray>", "<gray>[Commun]</gray>"),
UNCOMMON("Peu Commun", "<green>Peu Commun</green>", "<green>[Peu Commun]</green>"),
RARE("Rare", "<blue>Rare</blue>", "<blue>[Rare]</blue>"),
EPIC("Épique", "<light_purple>Épique</light_purple>", "<light_purple>[Épique]</light_purple>"),
LEGENDARY("Légendaire", "<gold>Légendaire</gold>", "<gold>[Légendaire]</gold>"),
MYTHIC("Mythique", "<gradient:#FF0055:#7A00FF>Mythique</gradient>", "<gradient:#FF0055:#7A00FF>[Mythique]</gradient>");
private final String displayName;
private final String formattedName;
private final String tag;
OreRarity(String displayName, String formattedName, String tag) {
this.displayName = displayName;
this.formattedName = formattedName;
this.tag = tag;
}
public String getDisplayName() {
return displayName;
}
public String getFormattedName() {
return formattedName;
}
public String getTag() {
return tag;
}
public static OreRarity fromString(String name) {
if (name == null) return COMMON;
try {
return OreRarity.valueOf(name.toUpperCase());
} catch (IllegalArgumentException e) {
return COMMON;
}
}
}
@@ -1,100 +0,0 @@
package fr.luc.gamingcore.mining.model;
public class PlacedOreBlock {
private int id;
private final String oreId;
private CustomOre oreType;
private final BlockLocation location;
private int currentStock;
private boolean depleted;
private long nextRespawnTimestamp;
public PlacedOreBlock(int id, String oreId, CustomOre oreType, BlockLocation location,
int currentStock, boolean depleted, long nextRespawnTimestamp) {
this.id = id;
this.oreId = oreId;
this.oreType = oreType;
this.location = location;
this.currentStock = currentStock;
this.depleted = depleted;
this.nextRespawnTimestamp = nextRespawnTimestamp;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getOreId() {
return oreId;
}
public CustomOre getOreType() {
return oreType;
}
public void setOreType(CustomOre oreType) {
this.oreType = oreType;
}
public BlockLocation getLocation() {
return location;
}
public int getCurrentStock() {
return currentStock;
}
public void setCurrentStock(int currentStock) {
this.currentStock = currentStock;
}
public boolean isDepleted() {
return depleted;
}
public void setDepleted(boolean depleted) {
this.depleted = depleted;
}
public long getNextRespawnTimestamp() {
return nextRespawnTimestamp;
}
public void setNextRespawnTimestamp(long nextRespawnTimestamp) {
this.nextRespawnTimestamp = nextRespawnTimestamp;
}
/**
* Decrements the stock by 1 and checks if it reached 0.
*
* @return true if the ore has just been depleted
*/
public boolean decrementStock() {
this.currentStock--;
if (this.currentStock <= 0) {
this.currentStock = 0;
this.depleted = true;
if (this.oreType != null && this.oreType.isRespawnable()) {
this.nextRespawnTimestamp = System.currentTimeMillis() + (this.oreType.getRespawnTimeSeconds() * 1000L);
}
return true;
}
return false;
}
/**
* Resets the stock and removes depleted flag on respawn.
*/
public void respawn() {
this.depleted = false;
this.nextRespawnTimestamp = 0L;
if (this.oreType != null) {
this.currentStock = this.oreType.getStock();
}
}
}
+5 -15
View File
@@ -3,20 +3,10 @@ version: 1.0.0
main: fr.luc.gamingcore.GamingCore
api-version: '1.16'
author: Luc
description: GamingCore Plugin - Système de Minage Personnalisé
description: GamingCore Plugin
depend:
- WorldGuard
softdepend:
- WorldEdit
commands:
gamingcore:
description: Commande principale de GamingCore
aliases: [gc, mining]
permission: gamingcore.use
usage: /<command> [help|ore|reload]
permissions:
gamingcore.use:
description: Permet d'utiliser les commandes de base de GamingCore
default: true
gamingcore.admin:
description: Permet d'administrer les minerais et recharger le plugin
default: op
@@ -1,58 +0,0 @@
package fr.luc.gamingcore.mining;
import com.cryptomorin.xseries.XMaterial;
import fr.luc.gamingcore.mining.model.*;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import static org.junit.jupiter.api.Assertions.*;
public class OreModelTest {
@Test
public void testOreStockAndDepletion() {
CustomOre ore = new CustomOre("test_iron", "Test Iron", XMaterial.IRON_ORE, 3.0,
OreRarity.COMMON, 3, true, 10, XMaterial.BEDROCK, new ArrayList<>());
BlockLocation loc = new BlockLocation("world", 100, 64, -200);
PlacedOreBlock placed = new PlacedOreBlock(1, ore.getId(), ore, loc, ore.getStock(), false, 0);
assertEquals(3, placed.getCurrentStock());
assertFalse(placed.isDepleted());
// First hit
boolean depleted1 = placed.decrementStock();
assertFalse(depleted1);
assertEquals(2, placed.getCurrentStock());
// Second hit
boolean depleted2 = placed.decrementStock();
assertFalse(depleted2);
assertEquals(1, placed.getCurrentStock());
// Third hit -> Depleted
boolean depleted3 = placed.decrementStock();
assertTrue(depleted3);
assertEquals(0, placed.getCurrentStock());
assertTrue(placed.isDepleted());
assertTrue(placed.getNextRespawnTimestamp() > System.currentTimeMillis());
// Respawn
placed.respawn();
assertFalse(placed.isDepleted());
assertEquals(3, placed.getCurrentStock());
assertEquals(0L, placed.getNextRespawnTimestamp());
}
@Test
public void testBlockLocationEquality() {
BlockLocation loc1 = new BlockLocation("world", 10, 20, 30);
BlockLocation loc2 = new BlockLocation("world", 10, 20, 30);
BlockLocation loc3 = new BlockLocation("nether", 10, 20, 30);
assertEquals(loc1, loc2);
assertEquals(loc1.hashCode(), loc2.hashCode());
assertNotEquals(loc1, loc3);
}
}