--- title: Événements Bloc type: docs weight: 3 --- Événements déclenchés par les interactions et modifications de blocs. Ces événements utilisent le pattern ECS (Entity Component System). ## Événements de Destruction & Placement ### BreakBlockEvent {{< badge "Annulable" >}} {{< badge "ECS" >}} Déclenché quand un bloc est en train d'être cassé. **Package :** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Champs,Méthodes,Exemple" >}} {{< tab >}} | Champ | Type | Description | |-------|------|-------------| | targetBlock | `Vector3i` | Position du bloc | | blockType | `BlockType` | Type de bloc | | itemInHand | `ItemStack` | Outil utilisé | {{< /tab >}} {{< tab >}} - `getTargetBlock()` - Retourne la position du bloc - `getBlockType()` - Retourne le type de bloc - `getItemInHand()` - Retourne l'outil utilisé - `isCancelled()` - Vérifie si annulé - `setCancelled(boolean)` - Annule le cassage {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(BreakBlockEvent.class, event -> { Vector3i position = event.getTargetBlock(); BlockType blockType = event.getBlockType(); ItemStack tool = event.getItemInHand(); // Protéger certains blocs if (blockType.getId().equals("special_ore")) { event.setCancelled(true); return; } // Logger le cassage getLogger().at(Level.INFO).log("Bloc cassé à " + position + ": " + blockType.getId()); }); ``` {{< /tab >}} {{< /tabs >}} --- ### PlaceBlockEvent {{< badge "Annulable" >}} {{< badge "ECS" >}} Déclenché quand un bloc est placé. **Package :** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Champs,Méthodes,Exemple" >}} {{< tab >}} | Champ | Type | Description | |-------|------|-------------| | targetBlock | `Vector3i` | Position où le bloc sera placé | | rotation | `RotationTuple` | Rotation/orientation du bloc | | itemInHand | `ItemStack` | Item bloc placé | {{< /tab >}} {{< tab >}} - `getTargetBlock()` - Retourne la position cible - `setTargetBlock(Vector3i)` - Change la position de placement - `getRotation()` - Retourne la rotation du bloc - `setRotation(RotationTuple)` - Change la rotation - `getItemInHand()` - Retourne l'item bloc - `isCancelled()` - Vérifie si annulé - `setCancelled(boolean)` - Annule le placement {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(PlaceBlockEvent.class, event -> { Vector3i position = event.getTargetBlock(); RotationTuple rotation = event.getRotation(); ItemStack item = event.getItemInHand(); // Vérifier les permissions de construction if (isProtectedArea(position)) { event.setCancelled(true); return; } // Forcer une certaine rotation event.setRotation(RotationTuple.of(Rotation.North, Rotation.None, Rotation.None)); }); ``` {{< /tab >}} {{< /tabs >}} --- ### DamageBlockEvent {{< badge "Annulable" >}} {{< badge "ECS" >}} Déclenché quand des dégâts sont appliqués à un bloc pendant le cassage (avant que le bloc ne soit complètement cassé). **Package :** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Champs,Méthodes,Exemple" >}} {{< tab >}} | Champ | Type | Description | |-------|------|-------------| | targetBlock | `Vector3i` | Position du bloc | | blockType | `BlockType` | Type de bloc | | itemInHand | `ItemStack` | Outil utilisé (peut être null) | | currentDamage | `float` | Dégâts déjà appliqués au bloc | | damage | `float` | Dégâts appliqués ce tick | {{< /tab >}} {{< tab >}} - `getTargetBlock()` - Retourne la position du bloc - `setTargetBlock(Vector3i)` - Change le bloc cible - `getBlockType()` - Retourne le type de bloc - `getItemInHand()` - Retourne l'outil utilisé - `getCurrentDamage()` - Retourne les dégâts accumulés - `getDamage()` - Retourne les dégâts ce tick - `setDamage(float)` - Modifie le montant de dégâts - `isCancelled()` - Vérifie si annulé - `setCancelled(boolean)` - Annule les dégâts {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(DamageBlockEvent.class, event -> { Vector3i position = event.getTargetBlock(); BlockType blockType = event.getBlockType(); // Rendre certains blocs indestructibles if (blockType.getId().equals("bedrock")) { event.setCancelled(true); return; } // Réduire les dégâts sur les blocs renforcés if (blockType.getId().startsWith("hardened_")) { event.setDamage(event.getDamage() * 0.5f); } // Logger la progression du minage float progress = event.getCurrentDamage() / 100f; getLogger().at(Level.INFO).log("Bloc " + blockType.getId() + " à " + progress + "% de dégâts"); }); ``` {{< /tab >}} {{< /tabs >}} --- ## Événements d'Interaction ### UseBlockEvent Classe de base pour les événements d'utilisation de bloc. A des variantes Pre et Post. **Package :** `com.hypixel.hytale.server.core.event.events.ecs` #### UseBlockEvent.Pre {{< badge "Annulable" >}} Déclenché avant qu'une action d'utilisation de bloc ne se produise (clic droit sur bloc). {{< tabs items="Champs,Méthodes,Exemple" >}} {{< tab >}} | Champ | Type | Description | |-------|------|-------------| | interactionType | `InteractionType` | Type d'interaction | | context | `InteractionContext` | Contexte d'interaction | | targetBlock | `Vector3i` | Position du bloc | | blockType | `BlockType` | Le type de bloc | {{< /tab >}} {{< tab >}} - `getInteractionType()` - Retourne le type d'interaction - `getContext()` - Retourne le contexte d'interaction - `getTargetBlock()` - Retourne la position du bloc - `getBlockType()` - Retourne le type de bloc - `isCancelled()` - Vérifie si annulé - `setCancelled(boolean)` - Annule l'action d'utilisation {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(UseBlockEvent.Pre.class, event -> { Vector3i position = event.getTargetBlock(); BlockType blockType = event.getBlockType(); InteractionContext context = event.getContext(); // Empêcher l'ouverture de conteneurs verrouillés if (isLocked(position)) { event.setCancelled(true); } }); ``` {{< /tab >}} {{< /tabs >}} --- #### UseBlockEvent.Post Déclenché après qu'une action d'utilisation de bloc s'est terminée avec succès. {{< tabs items="Champs,Méthodes,Exemple" >}} {{< tab >}} | Champ | Type | Description | |-------|------|-------------| | interactionType | `InteractionType` | Type d'interaction | | context | `InteractionContext` | Contexte d'interaction | | targetBlock | `Vector3i` | Position du bloc | | blockType | `BlockType` | Le type de bloc | {{< /tab >}} {{< tab >}} - `getInteractionType()` - Retourne le type d'interaction - `getContext()` - Retourne le contexte d'interaction - `getTargetBlock()` - Retourne la position du bloc - `getBlockType()` - Retourne le type de bloc {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(UseBlockEvent.Post.class, event -> { Vector3i position = event.getTargetBlock(); BlockType blockType = event.getBlockType(); // Suivre les interactions pour les quêtes if (isQuestBlock(blockType)) { completeQuestObjective("interact_with_" + blockType.getId()); } }); ``` {{< /tab >}} {{< /tabs >}} --- ## Événements d'Items ### DropItemEvent {{< badge "Annulable" >}} {{< badge "ECS" >}} Déclenché quand un item est lâché. A deux variantes : `Drop` et `PlayerRequest`. **Package :** `com.hypixel.hytale.server.core.event.events.ecs` #### DropItemEvent.Drop L'événement de drop réel avec les détails de l'item. {{< tabs items="Champs,Méthodes,Exemple" >}} {{< tab >}} | Champ | Type | Description | |-------|------|-------------| | itemStack | `ItemStack` | Item lâché | | throwSpeed | `float` | Vitesse du lancer | {{< /tab >}} {{< tab >}} - `getItemStack()` - Retourne le stack d'items - `setItemStack(ItemStack)` - Change l'item lâché - `getThrowSpeed()` - Retourne la vitesse de lancer - `setThrowSpeed(float)` - Change la vitesse de lancer - `isCancelled()` - Vérifie si annulé - `setCancelled(boolean)` - Annule le drop {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(DropItemEvent.Drop.class, event -> { ItemStack item = event.getItemStack(); // Empêcher de lâcher certains items if (item.getId().equals("quest_item")) { event.setCancelled(true); return; } // Modifier la vitesse de lancer event.setThrowSpeed(event.getThrowSpeed() * 1.5f); }); ``` {{< /tab >}} {{< /tabs >}} --- #### DropItemEvent.PlayerRequest Déclenché quand un joueur demande à lâcher un item (avant le drop réel). {{< tabs items="Champs,Méthodes,Exemple" >}} {{< tab >}} | Champ | Type | Description | |-------|------|-------------| | inventorySectionId | `int` | ID de la section d'inventaire | | slotId | `short` | Slot depuis lequel on drop | {{< /tab >}} {{< tab >}} - `getInventorySectionId()` - Retourne la section d'inventaire - `getSlotId()` - Retourne l'index du slot - `isCancelled()` - Vérifie si annulé - `setCancelled(boolean)` - Annule la requête {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(DropItemEvent.PlayerRequest.class, event -> { int sectionId = event.getInventorySectionId(); short slot = event.getSlotId(); // Empêcher de drop depuis certains slots if (sectionId == Inventory.HOTBAR && slot == 0) { event.setCancelled(true); // Ne peut pas drop le premier slot hotbar } }); ``` {{< /tab >}} {{< /tabs >}} --- ### InteractivelyPickupItemEvent {{< badge "Annulable" >}} {{< badge "ECS" >}} Déclenché quand un joueur ramasse un item de manière interactive (ramassage manuel, pas auto-pickup). **Package :** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Champs,Méthodes,Exemple" >}} {{< tab >}} | Champ | Type | Description | |-------|------|-------------| | itemStack | `ItemStack` | L'item ramassé | {{< /tab >}} {{< tab >}} - `getItemStack()` - Retourne le stack d'items - `setItemStack(ItemStack)` - Change l'item ramassé - `isCancelled()` - Vérifie si annulé - `setCancelled(boolean)` - Annule le ramassage {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(InteractivelyPickupItemEvent.class, event -> { ItemStack item = event.getItemStack(); // Empêcher de ramasser des items de quête qui n'appartiennent pas au joueur if (item.getId().startsWith("quest_") && !canPickupQuestItem(player, item)) { event.setCancelled(true); return; } // Transformer les items au ramassage if (item.getId().equals("raw_ore")) { event.setItemStack(item.withCount(item.getCount() * 2)); } }); ``` {{< /tab >}} {{< /tabs >}} --- ## Événements de Craft ### CraftRecipeEvent {{< badge "Annulable" >}} {{< badge "ECS" >}} Déclenché quand une recette de craft est exécutée. A des variantes Pre et Post. **Package :** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Champs,Méthodes,Exemple" >}} {{< tab >}} | Champ | Type | Description | |-------|------|-------------| | craftedRecipe | `CraftingRecipe` | La recette craftée | | quantity | `int` | Nombre d'items craftés | {{< /tab >}} {{< tab >}} - `getCraftedRecipe()` - Retourne la recette de craft - `getQuantity()` - Retourne la quantité craftée - `isCancelled()` - Vérifie si annulé (Pre uniquement) - `setCancelled(boolean)` - Annule le craft (Pre uniquement) {{< /tab >}} {{< tab >}} ```java // Vérification pré-craft getEventRegistry().register(CraftRecipeEvent.Pre.class, event -> { CraftingRecipe recipe = event.getCraftedRecipe(); int quantity = event.getQuantity(); // Bloquer certaines recettes if (isRestrictedRecipe(recipe)) { event.setCancelled(true); } }); // Suivi post-craft getEventRegistry().register(CraftRecipeEvent.Post.class, event -> { CraftingRecipe recipe = event.getCraftedRecipe(); int quantity = event.getQuantity(); // Suivre les statistiques de craft incrementCraftCount(recipe.getId(), quantity); }); ``` {{< /tab >}} {{< /tabs >}} --- ## Autres Événements ### SwitchActiveSlotEvent {{< badge "Annulable" >}} {{< badge "ECS" >}} Déclenché quand un joueur change son slot actif de hotbar. **Package :** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Champs,Méthodes,Exemple" >}} {{< tab >}} | Champ | Type | Description | |-------|------|-------------| | inventorySectionId | `int` | ID de la section d'inventaire | | previousSlot | `int` | Le slot avant le changement | | newSlot | `byte` | Le slot cible | | serverRequest | `boolean` | Si le serveur a initié le changement | {{< /tab >}} {{< tab >}} - `getInventorySectionId()` - Retourne la section d'inventaire - `getPreviousSlot()` - Retourne l'index du slot précédent - `getNewSlot()` - Retourne l'index du nouveau slot - `setNewSlot(byte)` - Change le slot cible - `isServerRequest()` - Vérifie si initié par le serveur - `isClientRequest()` - Vérifie si initié par le client - `isCancelled()` - Vérifie si annulé - `setCancelled(boolean)` - Annule le changement de slot {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(SwitchActiveSlotEvent.class, event -> { int fromSlot = event.getPreviousSlot(); int toSlot = event.getNewSlot(); // Empêcher le changement pendant un cooldown if (isOnCooldown(player)) { event.setCancelled(true); return; } // Suivre l'usage des slots pour analytics if (event.isClientRequest()) { trackSlotSwitch(player, fromSlot, toSlot); } }); ``` {{< /tab >}} {{< /tabs >}} --- ### DiscoverZoneEvent {{< badge "ECS" >}} Déclenché quand un joueur découvre une nouvelle zone. A une variante `Display` qui est annulable. **Package :** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Champs,Méthodes,Exemple" >}} {{< tab >}} | Champ | Type | Description | |-------|------|-------------| | discoveryInfo | `ZoneDiscoveryInfo` | Informations sur la zone découverte | {{< /tab >}} {{< tab >}} - `getDiscoveryInfo()` - Retourne les infos de découverte de zone - `isCancelled()` - Vérifie si annulé (Display uniquement) - `setCancelled(boolean)` - Annule l'affichage (Display uniquement) {{< /tab >}} {{< tab >}} ```java // Annuler l'affichage de notification de découverte de zone getEventRegistry().register(DiscoverZoneEvent.Display.class, event -> { WorldMapTracker.ZoneDiscoveryInfo info = event.getDiscoveryInfo(); // Cacher certaines zones de l'UI de découverte if (isHiddenZone(info)) { event.setCancelled(true); return; } // Logger la découverte pour les succès recordZoneDiscovery(player, info); }); ``` {{< /tab >}} {{< /tabs >}} --- ### ChangeGameModeEvent {{< badge "Annulable" >}} {{< badge "ECS" >}} Déclenché quand le mode de jeu d'une entité change. **Package :** `com.hypixel.hytale.server.core.event.events.ecs` {{< tabs items="Champs,Méthodes,Exemple" >}} {{< tab >}} | Champ | Type | Description | |-------|------|-------------| | gameMode | `GameMode` | Le nouveau mode de jeu | {{< /tab >}} {{< tab >}} - `getGameMode()` - Retourne le nouveau mode de jeu - `setGameMode(GameMode)` - Change le mode de jeu cible - `isCancelled()` - Vérifie si annulé - `setCancelled(boolean)` - Annule le changement de mode {{< /tab >}} {{< tab >}} ```java getEventRegistry().register(ChangeGameModeEvent.class, event -> { GameMode newMode = event.getGameMode(); // Empêcher le mode créatif dans certains mondes if (newMode == GameMode.CREATIVE && isRestrictedWorld(player.getWorld())) { event.setCancelled(true); player.sendMessage(Message.raw("Mode créatif non autorisé ici !")); return; } // Forcer le mode aventure if (shouldForceAdventure(player)) { event.setGameMode(GameMode.ADVENTURE); } getLogger().at(Level.INFO).log(player.getDisplayName() + " changé en " + newMode); }); ``` {{< /tab >}} {{< /tabs >}} --- ## Exemples Pratiques ### Plugin Logger de Blocs ```java public class BlockLoggerPlugin extends JavaPlugin { @Override public void start() { getEventRegistry().register(PlaceBlockEvent.class, event -> { logAction("PLACE", event.getTargetBlock(), event.getItemInHand()); }); getEventRegistry().register(BreakBlockEvent.class, event -> { logAction("BREAK", event.getTargetBlock(), event.getBlockType()); }); } private void logAction(String action, Vector3i pos, Object data) { getLogger().at(Level.INFO).log(String.format("%s: %d,%d,%d - %s", action, pos.x(), pos.y(), pos.z(), data)); } } ``` ### Système de Protection de Région ```java public class RegionPlugin extends JavaPlugin { private final Set regions = new HashSet<>(); @Override public void start() { // Protéger contre le cassage getEventRegistry().register(EventPriority.EARLY, BreakBlockEvent.class, event -> { if (isInProtectedRegion(event.getTargetBlock())) { event.setCancelled(true); } }); // Protéger contre le placement getEventRegistry().register(EventPriority.EARLY, PlaceBlockEvent.class, event -> { if (isInProtectedRegion(event.getTargetBlock())) { event.setCancelled(true); } }); // Protéger contre l'utilisation getEventRegistry().register(EventPriority.EARLY, UseBlockEvent.Pre.class, event -> { if (isInProtectedRegion(event.getTargetBlock())) { event.setCancelled(true); } }); } private boolean isInProtectedRegion(Vector3i pos) { return regions.stream().anyMatch(r -> r.contains(pos)); } } ``` ### Système de Minerais Custom ```java public class CustomOrePlugin extends JavaPlugin { private final Random random = new Random(); @Override public void start() { getEventRegistry().register(BreakBlockEvent.class, event -> { BlockType blockType = event.getBlockType(); if (isCustomOre(blockType)) { // Appliquer le multiplicateur fortune ItemStack tool = event.getItemInHand(); int fortuneLevel = getFortuneLevel(tool); int dropMultiplier = 1 + random.nextInt(fortuneLevel + 1); // Programmer les drops custom Vector3i pos = event.getTargetBlock(); scheduleOreDrops(pos, blockType, dropMultiplier); } }); } private boolean isCustomOre(BlockType blockType) { return blockType.getId().startsWith("custom_ore_"); } } ``` ### Système Anti-Grief ```java public class AntiGriefPlugin extends JavaPlugin { private final Map recentActions = new HashMap<>(); @Override public void start() { // Suivre les cassages de blocs pour rollback getEventRegistry().register(BreakBlockEvent.class, event -> { Vector3i pos = event.getTargetBlock(); BlockType blockType = event.getBlockType(); recentActions.put(pos, new BlockAction( ActionType.BREAK, blockType, System.currentTimeMillis() )); }); // Suivre les placements de blocs pour rollback getEventRegistry().register(PlaceBlockEvent.class, event -> { Vector3i pos = event.getTargetBlock(); recentActions.put(pos, new BlockAction( ActionType.PLACE, null, System.currentTimeMillis() )); }); } public void rollback(World world, int seconds) { long cutoff = System.currentTimeMillis() - (seconds * 1000L); recentActions.entrySet().stream() .filter(e -> e.getValue().timestamp() >= cutoff) .forEach(e -> { Vector3i pos = e.getKey(); BlockAction action = e.getValue(); if (action.type() == ActionType.PLACE) { // Supprimer les blocs placés world.breakBlock(pos.x(), pos.y(), pos.z()); } else if (action.blockType() != null) { // Restaurer les blocs cassés world.setBlock(pos.x(), pos.y(), pos.z(), action.blockType()); } }); recentActions.clear(); } record BlockAction(ActionType type, BlockType blockType, long timestamp) {} enum ActionType { BREAK, PLACE } } ``` ## Bonnes Pratiques {{< callout type="info" >}} **Directives pour les Événements de Bloc :** - Utiliser `EventPriority.EARLY` pour les systèmes de protection - Les événements ECS fournissent un contrôle détaillé sur les opérations de bloc - Toujours valider les positions avant de modifier les blocs - Considérer l'état de chargement des chunks pour les blocs distants - Nettoyer les données de suivi quand elles ne sont plus nécessaires {{< /callout >}} {{< callout type="warning" >}} **Note de Performance :** Les événements de bloc peuvent se déclencher très fréquemment. Évitez les opérations coûteuses dans les handlers et mettez en cache les résultats si possible. {{< /callout >}}