Idée WorldGuardInteractExt : ajouter des permissions sur WorldGuard

Feneck91

Architecte en herbe
2 Avril 2020
11
0
61
54
Bonjour à toutes et à tous.

J'utilise WorldGuard pour protéger un monde et cela fonctionne plutôt pas mal.
Toutefois, le principal problème de WorldGuard c'est l'impossibilité de modifier un bloc : pour pouvoir le modifier, il faut avoir une zone où casser / placer un bloc est autorisé.
J'ai donc des toutes petites zones où tous les droits sont permis, mais du coup, au lieu de poser un livre sur un pupitre, on peut casser le pupitre.
Idem pour un feu de camps, un champ, etc.
Soit j'ai pas compris un truc, soit c'est vraiment pénalisant.

J'ai donc décidé de voir si la création d'un plugin pourrait m'aider.
Je me suis donc lancé dans le développement d'un plugin, je suis ingénieur en informatique, je connais plein de langages dont Java, j'ai 54 ans.

Ce que je cherche ici serait un support, un échange. Pas certain que ce soit le bon endroit, les réponses dates de quelques mois souvent montrant un forum pas super réactif.
Si vous avez un autre forum plus spécialisé, je suis preneur.

J'ai déjà posté ici : https://www.spigotmc.org/threads/worldguardinteractext-plugin.707130/
Mais sans réponse.

Voici le repo Github, pour le moment le code est un peu en vrac (quelques blocs encore en commentaire), il devrait fonctionner à 90% pour la gestion des feux.
Mon repo : https://github.com/Feneck91/WorldGuardInteractExt

Pour le moment ça fonctionne bien, j'ai juste le message de WorldGuard qui me dit que l'action n'est pas autorisée alors que je la réactive.
ChatGPT me prosose :
Java:
public class CustomMessageSuppressHandler extends Handler {
    public static final Factory FACTORY = new Factory();

    public static class Factory extends Handler.Factory<CustomMessageSuppressHandler> {
        @Override
        public CustomMessageSuppressHandler create(Session session) {
            return new CustomMessageSuppressHandler(session);
        }
    }

    protected CustomMessageSuppressHandler(Session session) {
        super(session);
    }

    @Override
    public boolean onInteraction(Player player, Location clicked) {
        // Ne jamais afficher les messages pour notre flag personnalisé
        if (yourPluginAllowsThis(player, clicked)) {
            // On empêche WorldGuard d’envoyer le message
            return true; // true = ignorer la protection ici
        }
        return false;
    }

    private boolean yourPluginAllowsThis(Player player, Location loc) {
        // Même logique que dans ton listener
        RegionQuery query = WorldGuard.getInstance().getPlatform().getRegionContainer().createQuery();
        return query.testState(BukkitAdapter.adapt(loc), BukkitAdapter.adapt(player), WorldGuardInteractExt.INTERACT_EXT_FLAG);
    }
}

Vous en pensez quoi ?

Voilà si des personnes sont intéressées par ce plugin qui ne gère actuellement que les feux, n'hésitez pas à me donner des conseils.
Cordialement.

Merci à tous.
 
Dernière édition:
Salut,

J'ai rapidement regardé ton github. Désolé de le dire, mais on voit que tu as la 50ène... xD Les variables préfixé b_test pour dire que c'est un boolean... ça ne se fait plus. On a des IDE maintenant qui en un click ou un raccourci clavier nous disent le type de variable qu'on manipule. On ne préfix plus.

Bref, je suis pas sûr de comprendre le problème...
Tu as des flags de base dans WorldGuard pour gérer les interractions (typiquement les portes): https://worldguard.enginehub.org/en/latest/regions/flags/#protection-related
Tu peux déjà dire à WorldGuard que tu veux protéger une zone de la destruction (comportement de base) mais autoriser les interractions (avec le flag "interact"). Tu as aussi "use" ou "lighter".

Bon, en imaginant que les flags de base de WorldGuard ne te sufisent pas, je ne comprend pas bien ton code. Enfin, ce que je veux dire c'est que WorldGuard propose déjà une API: https://worldguard.enginehub.org/en/latest/developer/regions/custom-flags/
Et je ne retrouve rien de tout ça dans ton code... Après j'ai pas cherché longtemps hein, je suis peut être passé à côté.


Bon courrage dans ton développement,
Cordialement,
Detobel36
 
Le prefixage des variables… on pourrait en disserter des jours et des jours, c’est comme les tabs et les espaces.
C’est mon code et même si on ne fait plus comme ça, j’aime voir les types des variables sans IDE et même sous l’IDE d’un simple coup d’œil. Et il y a 35 ans, je prefixais déjà et l’IDE était déjà Visual C++ 1.5 et il te donnait déjà les types de variables.
Pour le reste : allumer un feux, placer un livre sur un pupitre, prendre de l’eau dans un chaudron, etc. Tout ça demande demande a casser un bloc, les autorisations use ne suffisent pas !
Avec la dernière version de WorldGuard je ne comprend pas comment utiliser les custom flags.
Je suis sur tel, je ferais une réponse plus complète dans la journée.
Après … j’ai peut être raté des flags, ce serait bien d’ailleurs…
D’un autre côté je pourrais ajouter des droits plus spécifiques : genre éteindre un feu qu’avec une pelle en diamant ou autoriser de poser un livre sur un pupitre : seulement un type de livre…
 
Dernière édition:
Salut,

Okay, je comprend mieux le soucis. Si c'est juste le fait de placer et casser les blocks il y a "block-break" et "block-place" mais ça ne permet pas de faire des conditions custom ;)

Du coup pour moi le problème peut être découpé en 2 parties:
1. Détecter l'event que tu veux autoriser ou non dans une région
2. Lié cette détection avec WorldGuard


Pour le point 1, c'est uniquement des events Minecraft/Spigot... On peut se pencher dessus si besoin mais a priori WorldGuard ne vient pas jouer un role la dedans.
Pour le point 2, il faut déclarer les nouveaux flags et ensuite les prendre en compte lors d'événement détecté lors du point précédent pour savoir si l'event doit être cancel (bloqué) ou non.
Il faut notamment se pencher sur la manière dont WorldGuard gère les flags: https://worldguard.enginehub.org/en/latest/developer/regions/flag-calculation/

En me plongeant dans le code source de WorldGuard, je vois par exemple que si on autorise explicitement un event WorldGuard ne le traite pas: https://github.com/EngineHub/WorldG...t/listener/RegionProtectionListener.java#L244

Enfin, si les règles sont si spécifique, est-ce que ça a encore du sens d'utiliser WorldGuard ? Je veux dire, est-ce que ton cas d'usage utilise les régions définie avec WorldGuard ? Si non, alors ça vaut peut être la peine de faire un plugin independant, qui défini une ou deux zone en fonction des besoins et implémente directment la logique (sans avoir toute la surcouche assez lourde de WorldGuard).


Cordialement,
Detobel36
 
Bon, me voilà sur PC, plus simple et plus rapide je suis en vacances.
Tout d'abord, merci d'avoir répondu à mon message.
Ensuite, j'utilise WorldGuard pour un tas de chose : protéger mon monde, éviter les dégâts des creepers, la propagation du feu, etc etc !
WorldGuard est vraiment un super instrument de protection du monde, donc non je ne peux pas m'en passer sauf à laisser les gens casser le monde (et la redstone est fragile) et l'abandonner aux Kevin.

Ensuite, je me suis aperçu que la protection des blocs ( "block-break" et "block-place" ) empèche le prélèvement avec un seau des chaudrons (eau lave neige), la dépose d'un livre sur un pupitre, l'extinction et l'allumage d'un feu, l'utilisation des champs (semer / récolter). Bref tout ce qui modifie un bloc et donc entraîne pour moi, la création de toutes petites zones où j'ajoute "block-break" et "block-place", ce qui veut dire aussi, qu'au lieu de prélever dans un chaudron on peut aussi, hélas, le casser.

Bref, WorldGuard c'est bien mais pas parfait, d'où l'idée de ce plugin. Ensuite, si j'y arrive, autant ajouter des droits un peu évolués, du genre ne pouvoir éteindre un feu qu'avec tel outils ou ne pouvoir planter que du blé, etc est juste un truc en plus qui ne coûte pas cher en dev.

J'ai déjà réussi à faire ce que je voulais pour le feu : j'annule AVANT et puis réactive après sur quelques évènements mais du coup, ça marche pour WorldGuard mais ça pourrait aussi casser d'autres plugins.
Donc l'idée est de passer par des customs flags et j'avoue que je n'ai pas réussi à comprendre comment ça marche. Je me retrouve avec des classes d'events qui propose une API tellement light qu'on ne peut rien faire.

Bref, j'essaie de comprendre...

Voici par exemple la protection de mon château et du feu de camp :
YAML:
#
# WorldGuard regions file
#
# WARNING: THIS FILE IS AUTOMATICALLY GENERATED. If you modify this file by
# hand, be aware that A SINGLE MISTYPED CHARACTER CAN CORRUPT THE FILE. If
# WorldGuard is unable to parse the file, your regions will FAIL TO LOAD and
# the contents of this file will reset. Please use a YAML validator such as
# http://yaml-online-parser.appspot.com (for smaller files).
#
# REMEMBER TO KEEP PERIODICAL BACKUPS.
#
regions:
  feu_cheminee_chateau:
    min:
      {
        x: 786,
        y: 63,
        z: 487
      }
    max:
      {
        x: 790,
        y: 66,
        z: 490
      }
    members:
      {
      }
    flags:
      {
        game-mode: survival,
        deny-message: vous n'êtes pas TT autorisé à réaliser cette action,
        tnt: deny,
        tnt-group: NON_MEMBERS,
        enderman-grief: deny,
        enderpearls: deny,
        lava-fire: deny,
        ride: allow,
        fire-spread: deny,
        creeper-explosion: deny,
        interact: allow,
        interact-group: NON_MEMBERS,
        use: allow,
        use-group: NON_MEMBERS,
      }
    owners:
      {
      }
    type: cuboid
    priority: 20
  chateau:
    min:
      {
        x: 700,
        y: -64,
        z: 300
      }
    max:
      {
        x: 834,
        y: 319,
        z: 515
      }
    members:
      {
      }
    flags:
      {
        game-mode: survival,
        deny-message: vous n'êtes pas autorisé à réaliser cette action,
        greeting: Bienvenue dans le château. Pour les chevaliers les plus téméraires de grandes récompenses vous attendent...,
        farewell: Vous quittez le château...,
        tnt: deny,
        tnt-group: NON_MEMBERS,
        enderman-grief: deny,
        enderpearls: deny,
        lava-fire: deny,
        ride: allow,
        fire-spread: deny,
        creeper-explosion: deny,
        chest-access: allow,
        chest-access-group: NON_MEMBERS,
        item-frame-rotation: allow,
        entity-item-frame-destroy: deny,
        entity-item-frame-destroy-group: NON_MEMBERS,
        item-frame-rotation-group: NON_MEMBERS,
        entity-painting-destroy: allow,
        entity-painting-destroy-group: NON_MEMBERS,
        interact: allow,
        interact-group: NON_MEMBERS,
        use: allow,
        use-group: NON_MEMBERS,
        bucket: allow,
        bucket-group: NON_MEMBERS,
      }
    owners:
      {
      }
    type: cuboid
    priority: 10
 
Dernière édition:
Bonjour,

Comme dit, si tu définis explicitement le résultat à autorisé (au lieu que défaut), WorldGuard ignorera la vérification complètement :
Java:
@EventHandler(priority = EventPriority.LOW)
public void onBlockInteraction(UseBlockEvent e) {
  final @Nullable RegionManager mgr = WorldGuard.getInstance().getPlatform().getRegionContainer().get(e.getWorld());
  final @Nullable ProtectedRegion castle = mgr.getRegion("château");
  
  if(e.getBlocks().stream().all(b -> castle.contains(b.getX(), b.getY(), b.getZ()) && /* est pupitre */) {
    e.setResult(Result.ALLOW);
  }
}

Alternativement, tu peux annuler l'annulation après coup :
Java:
@EventHandler(priority = EventPriority.HIGHEST /* WG écoute en HIGH */, ignoreCancelled = false)
public void onPlayerInteract(PlayerInteractEvent e) {
  if(/* ... */) {
    e.setCancelled(false);
  }
}

Les variables préfixé b_test pour dire que c'est un boolean... ça ne se fait plus. On a des IDE maintenant qui en un click ou un raccourci clavier nous disent le type de variable qu'on manipule. On ne préfix plus.
En soit tant que c'est consistant, la notation hongroise pose pas vraiment problème ; tout le monde n'utilise pas une IDE et je ne pense pas que GitLab indexe le code source (et même, GitHub n'est pas tip-top pour l'instant).

Cordialement,
ShE3py
 
c’est peut-être le ALLOW qu’il me manque.
Annuler après coup ne fonctionne qu’en parti : le message « vous n’avez pas le droit d’effectuer cette action » s’affiche quand même et l’action est effectuée : bof.
Mais ce n’utilise pas les customs flags et ça peut influer sur les autres plugins qui pourrait être installés

Bon, j'ai acheté Minecraft Java, j'ai testé avec UseBlockEvent et _event.setResult(Event.Result.ALLOW). Ca met le message et ça ne fonctionne pas.

Là, je suis passé sur quelque chose de plus pro : les customs flags mais ça ne fonctionne pas du tout car je ne sais pas comment interagir avec WorldGuard pour lui dire de laisser passer certaines actions.

Non, ce n'est pas si simple !

En fait, je fais ça :
Java:
/**
     * When player make event.
     *
     * Check if it must be uncanceled.
     *
     *
     * @param _event The event
     */
    @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = false)
    public void onPlayerInteractLowest(PlayerInteractEvent _event)
    {
        m_materialConfig.clearNextPlaceEventInfos();
        // Only if WorldGuard has canceled the interaction, else do nothing
        Block block = _event.getClickedBlock();
        if (block != null)
        {
            if (_event.getHand() == EquipmentSlot.HAND)
            {   // Remove 2 call with OFF_HAND
                if (m_materialConfig.manageEvent(_event))
                {
                    //Get WG session for the player
                    SessionManager sessionManager = WorldGuard.getInstance().getPlatform().getSessionManager();
                    Session session = sessionManager.get((LocalPlayer) BukkitAdapter.adapt(_event.getPlayer()));

                    // Récupérer ton handler et stocker la décision
                    InteractionManagerHandler handler = session.getHandler(InteractionManagerHandler.class);
                    if (handler != null)
                    {
                        handler.setPendingDecision(m_materialConfig);
                    }
                    if (IsVerboseLogEnabled())
                    {
                        getLogger().info("Player interaction is validated!");
                    }
                }
            }
        }
    }

Le problème c'est qu'ensuite j'ai une classe qui dérive de com.sk89q.worldguard.session.handler (ici InteractionManagerHandler) et franchement on en fait rien de ce truc !
Presque aucune fonction à redéfinir, qui n'a rien à voir avec les interactions :
Java:
public abstract class Handler {
    private final Session session;

    protected Handler(Session session) {
        Preconditions.checkNotNull(session, "session");
        this.session = session;
    }

    public Session getSession() {
        return this.session;
    }

    public void initialize(LocalPlayer player, Location current, ApplicableRegionSet set) {
    }

    public void uninitialize(LocalPlayer player, Location current, ApplicableRegionSet set) {
    }

    public boolean testMoveTo(LocalPlayer player, Location from, Location to, ApplicableRegionSet toSet, MoveType moveType) {
        return true;
    }

    public boolean onCrossBoundary(LocalPlayer player, Location from, Location to, ApplicableRegionSet toSet, Set<ProtectedRegion> entered, Set<ProtectedRegion> exited, MoveType moveType) {
        return true;
    }

    public void tick(LocalPlayer player, ApplicableRegionSet set) {
    }

    @Nullable
    public StateFlag.State getInvincibility(LocalPlayer player) {
        return null;
    }

    public abstract static class Factory<T extends Handler> {
        public abstract T create(Session var1);
    }
}

C'est là où j'en suis. Pour éteindre / allumer un feu de camp, je sais charger la configuration, puis dans les events tester les outils, tester ce que l'utilisateur fait, s'il a le droit, etc. Une fois que je sais qu'il a le droit, je ne sais pas la dire à WorldGuard.
Soit je prend tous les events et je bloque WG mais ça peut bloquer les autres plugins, soit je le fais après coup mais le message indiquant qu'on a pas le droit s'affiche quand même, et la méthode soit disant "propre" en utilisant des custom flags, je ne sais pas comment les utiliser pour interagir avec WG.
 
Dernière édition:
Salut,

Ensuite, j'utilise WorldGuard pour un tas de chose : protéger mon monde, éviter les dégâts des creepers, la propagation du feu, etc etc !
WorldGuard est vraiment un super instrument de protection du monde, donc non je ne peux pas m'en passer sauf à laisser les gens casser le monde (et la redstone est fragile) et l'abandonner aux Kevin.
Le vrai intéret de worldguard c'est de gérer des zones.
Si c'est juste une protection de ton monde au complet, il y a d'autres plugins ;)

Et quand bien même, ma question n'était pas de supprimer worldguard. C'était d'intégrer ton développement dans worldguard. Cela n'a d'intéret que si tu veux limiter les restrictions à des zones. Si ça s'applique sur tout ton monde, aucun intéret de lier ça à WorldGuard.


et ça peut influer sur les autres plugins qui pourrait être installés
Je suis pas sûr de comprend le problème que tu essaies de présenter ici..
Que tu passes par l'API worldguard ou pas, ça va influencer les autres plugins déjà installé. D'où le fait que spigot propose des priorités dans les events.

Concernant ton code, pour moi, de ce que j'ai compris, tu bloques l'event avec un simplement event.setCancelled(true).
C'est pas le flag worldguard qui bloque... Worldguard il va juste te dire, pour une coordonnée, dans quelle zone tu es et quel sont les flags qui sont activé dans cette zone. C'est à toi après, sur base des flags reçu par WorldGuard que tu décides de bloquer l'event ou non.



Cordialement,
Detobel36
 
Bon, vous m'expliquez que je n'ai pas besoin de ci, de ça, mais pas vraiment d'aide.
Oui, dans mon monde j'ai plein de zones : le château, le village, des cabanes...

Pour expliquer ce que je peux faire pour que ça fonctionne :
  • pour éteindre un feu :
    • PlayerInteractEvent (priority LOW) mets event.setCancelled(true); pour éviter que cet event soit géré par WG
    • PlayerInteractEvent (priority HIGHEST) mets event.setCancelled(false); pour continuer d'éteindre le feu
    • BlockPlaceEvent(priority LOW) mets event.setCancelled(true); pour éviter que cet event soit géré par WG
    • BlockPlaceEvent(priority HIGHEST) mets event.setCancelled(false); pour continuer d'éteindre le feu, Minecraft éteint alors le feu
  • pour allumer un feu :
    • BlockIgniteEvent (priority LOW) mets event.setCancelled(true); pour éviter que cet event soit géré par WG
    • BlockIgniteEvent (priority HIGHEST) mets event.setCancelled(false); pour continuer d'éteindre le feu
    • BlockPlaceEvent(priority LOW) mets event.setCancelled(true); pour éviter que cet event soit géré par WG
    • BlockPlaceEvent(priority HIGHEST) mets event.setCancelled(false); pour continuer d'éteindre le feu, Minecraft éteint alors le feu
Si j'enlève un ce ces event, WG indique "vous n'avez pas le droit bla bla bla" et le feu ne s'allume pas ou ne s'éteint pas.
Ce n'est d'ailleurs pas complètement vrai : si j'enlève PlayerInteractEvent (priority LOW) et uncancel l'event dans (priority HIGHEST) alors le feu peut être éteint MAIS WG indique quand même "vous n'avez pas le droit bla bla bla" ce qui fait bizarre.

Visiblement l'event UseBlockEvent est aussi appelé (je ne sais pas a quel moment) et faire event.setResult(Event.Result.ALLOW); ne résous pas le problème.

Pourquoi je dis que ça va influer sur les autres plugins ? Parce qu'en faisant ça (bien entendu, QUE lorsque je détecte un évènement intéressant), tous les plugins qui auraient besoin de ces events à ce moment là et qui n'ont pas ignoreCancelled = true dans l'event @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true / false) ne seront pas appelés.

EDIT : J'ai essayé le flag lighter et ça ne sert à rien pour allumer et éteindre un feu de camp.

Bref la bonne solution est d'utiliser les custom flags qui n'impacteront QUE WG mais .. comment ça marche ce truc ?
 
Dernière édition:
Pourrais-tu donner un exemple concret de ce que tu veux faire ? Par exemple, autoriser les joueurs à prendre le livre d'un pupitre.

Je suppose que gérer PlayerInteractEvent à Result.ALLOW fonctionne ? Sans se soucier de la zone ou de flags pour l'instant.

Pour le flag custom, le plus simple serait probablement de forker WorldGuard et de rajouter use-lectern en imitant use-anvil :
https://github.com/EngineHub/WorldG...tener/RegionProtectionListener.java#L274-L277

Sans fork, il faudrait copier-coller les bouts de code non-public.