当前位置: 首页 > 知识库问答 >
问题:

如何查看bukkit插件中调用setCancelled()的类/插件?

澹台奇略
2023-03-14

我有一个自定义的事件在我的bukkit/龙头插件,扩展PlayerInteract事件,试图打开箱子在附近的地区周围的球员。

目前代码使用这个事件来确保没有其他插件(例如悲伤预防)反对玩家打开箱子。如果玩家可以打开箱子,我的插件会尝试将物品放入箱子。我想忽略< code>setCancelled(),如果它被某个插件(理想情况下)或类(作为一种解决方法)调用的话

从这个问题中我可以看出,要获得我可以使用的类

String callerClassName = new Exception().getStackTrace()[1].getClassName();
String calleeClassName = new Exception().getStackTrace()[0].getClassName();

来获取类名。或者,我可以在这个调用周围使用一些东西:

StackTraceElement[] stElements = Thread.currentThread().getStackTrace();

然而,关于这个问题的所有评论都表明,除了这一点之外,可能还有更好的方法可以做到这一点。

布基特有更好的方法吗?

作为参考,这是我的自定义玩家互动事件的全部内容:

public class FakePlayerInteractEvent extends PlayerInteractEvent {
    public FakePlayerInteractEvent(Player player, Action rightClickBlock, ItemStack itemInHand, Block clickedBlock, BlockFace blockFace) {
        super(player, rightClickBlock, itemInHand, clickedBlock, blockFace);
    }
}

以及围绕事件使用的代码

PlayerInteractEvent fakeEvent = AutomaticInventory.getInstance().new FakePlayerInteractEvent(player, Action.RIGHT_CLICK_BLOCK, player.getInventory().getItemInMainHand(), block, BlockFace.UP);
Bukkit.getServer().getPluginManager().callEvent(fakeEvent);
if(!fakeEvent.isCancelled()){ ... do stuff }

共有2个答案

凤扬
2023-03-14

我建议使用优先级

优先级按以下顺序排列:

  1. 最低
  2. 正常
  3. 最高
  4. 监视器

如果您将事件优先级设置为HIGHESTMONITOR,您的事件将在所有其他优先级都已侦听该事件后侦听该事件。例如,即使另一个插件试图取消该事件,您仍然可以侦听该事件。

注意:不建议更改具有监视优先级的事件的结果。它应仅用于监视。

更改事件优先级(默认值:正常)

@EventHandler (priority = EventPriority.HIGHEST)
public void onEvent(Event e) {
}

如果你想让其他插件最终跟随你的插件,例如,如果你希望World Edit比你的插件“更强”,请将优先级设置为LOWLOWEST。如果你想让你的插件说了算,那就提高优先级。

@编辑

如果你想在没有优先级的情况下做这件事,并且确实需要识别正在取消的插件,bukkit论坛的Comprehenix为你提供了一个解决方案。请记住,这是不推荐的

如何做到这一点的示例:

public class ExampleMod extends JavaPlugin implements Listener {

private CancellationDetector<BlockPlaceEvent> detector = new CancellationDetector<BlockPlaceEvent>(BlockPlaceEvent.class);

    @Override
    public void onEnable() {
        getServer().getPluginManager().registerEvents(this, this);

        detector.addListener(new CancelListener<BlockPlaceEvent>() {
            @Override
            public void onCancelled(Plugin plugin, BlockPlaceEvent event) {
                System.out.println(event + " cancelled by " + plugin);
            }
        });
    }

    @Override
    public void onDisable() {
        // Incredibly important!
        detector.close();
    }

    // For testing
    @EventHandler
    public void onBlockPlaceEvent(BlockPlaceEvent e) {
        e.setCancelled(true);
    }
}

你可以在这里找到CancellationDetector的git

梁韬
2023-03-14

好问题!暂时让我忽略引发这个问题的原因。Bukkit没有“发布”确定事件取消来源的方法。然而,您“评估”事件的方法是正确的。

正如您已经知道或怀疑的那样,使用堆栈跟踪不是一个好的解决方案。它们在生成和描述可能不一定保证保持不变的特定于实现的细节方面相对昂贵。更好的方法是模拟Bukkit在调用callEvent()时使用的事件触发过程。

虽然Bukkit API不能保证事件触发过程的实现,但它已经稳定了很多年,没有太大变化。在过去的5年里,它一直在为我们工作,当电话事件()被拆分为电话事件()/火事件()时,只需要一次轻微的重构。

我希望我能给你整个EventUtils帮助程序类,但由于版权问题,我不得不编辑它。我确实验证了这个简化的类是否通过了适当的单元测试。您或其他任何人都可以根据需要自由使用此代码。它的评论更详细地解释了操作。我应该指出,我们使用Doxygen而不是JavaDoc来生成文档。

public class EventUtils {

    /**
     * @brief Determine if the given event will be cancelled.
     * 
     * This method emulates Bukkit's SimplePluginManager.fireEvent() to evaluate whether it will
     * be cancelled. This is preferred over using callEvent() as this method can limit the scope
     * of evaluation to only plugins of interest. Furthermore, this method will terminate as soon
     * as the event is cancelled to minimize any *side effects* from plugins further down the event
     * chain (e.g. mcMMO). No evaluation will be performed for events that do not
     * implement Cancellable.
     * 
     * The given plugin set is interpreted as either an Allow set or a Deny set, as follows:
     * 
     * - \c allowDeny = \c false - Allow mode. Only enabled plugins included in the given plugin
     *   set will be evaluated.
     * - \c allowDeny = \c false - Deny mode. Only enabled plugins *not* included in the given
     *   plugin set will be evaluated.
     * 
     * @warning Care should be taken when using this method from within a plugin's event handler for
     * the same event or event type (e.g. "faked" events). As this may result in an unending
     * recursion that will crash the server. To prevent this situation, the event handler should
     * (given in order of preference): 1) restrict evaluation to a specific Allow set not including
     * its own plugin; or, 2) add its own plugin to a Deny set. See overloaded convenience methods
     * for more details.
     * 
     * @param evt event under test
     * @param plugins Allow/Deny plugin set
     * @param allowDeny \c false - evaluate using an Allow set; or \c true - evaluate using a
     *        Deny set.
     * @return first plugin that cancelled given event; or \c if none found/did
     */

    public static Plugin willCancel( Event evt, Set<Plugin> plugins, boolean allowDeny ) {
        PluginManager piMgr = Bukkit.getPluginManager();

        /*
         * 1. From SimplePluginManager.callEvent(). Check thread-safety and requirements as if this
         * were a normal event call.
         */
        if ( evt.isAsynchronous() ) {
            if ( Thread.holdsLock( piMgr ) ) {
                throw new IllegalStateException( evt.getEventName()
                        + " cannot be triggered asynchronously from inside synchronized code." );
            }
            if ( Bukkit.isPrimaryThread() ) {
                throw new IllegalStateException( evt.getEventName()
                        + " cannot be triggered asynchronously from primary server thread." );
            }
            return fireUntilCancelled( evt, plugins, allowDeny );
        }
        else {
            synchronized ( piMgr ) {
                return fireUntilCancelled( evt, plugins, allowDeny );
            }
        }

    }


    /**
     * @brief See willCancel() for details.
     * 
     * @note Scoped as `protected` method for unit testing without reflection.
     * 
     * @param evt event under test
     * @param plugins Allow/Deny plugin set
     * @param allowDeny \c false - evaluate using an Allow set; or \c true - evaluate using a
     *        Deny set.
     * @return first plugin that cancelled given event; or \c if none found/did
     */
    protected static Plugin fireUntilCancelled( Event evt, Set<Plugin> plugins, boolean allowDeny ) {

        /*
         * 1. If event cannot be canceled, nothing will cancel it.
         */

        if ( !(evt instanceof Cancellable) )
            return null;

        /*
         * 2. Iterate over the event's "baked" event handler list.
         */

        HandlerList handlers = evt.getHandlers();
        for ( RegisteredListener l : handlers.getRegisteredListeners() ) {

            /*
             * A. Is associated plugin applicable? If not, move to next listener.
             */

            if ( !ofInterest( l.getPlugin(), plugins, allowDeny ) )
                continue;

            /*
             * B. Call registered plugin listener. If event is marked cancelled afterwards, return
             * reference to canceling plugin.
             */

            try {
                l.callEvent( evt );
                if ( ((Cancellable) evt).isCancelled() )
                    return l.getPlugin();
            }
            catch ( EventException e ) {

                /*
                 * Can be safely ignored as it is only used to nag developer about legacy events
                 * and similar matters.
                 */
            }
        }
        return null;
    }


    /**
     * @brief Determine whether the given plugin is of interest.
     * 
     * This method determines whether the given plugin is of interest. A plugin is of no interest
     * if any of the following conditions are met:
     * 
     * - the plugin is disabled
     * - \c allowDeny is \c false (allow) and set does not contains plugin
     * - \c allowDeny is \c true (deny) and set contains plugin
     * 
     * @note Scoped as `protected` method for unit testing without reflection.
     * 
     * @param plugin plugin to evaluate
     * @param plugins plugin allow/deny set
     * @param allowDeny \c false validate against allow set; \c true validate against deny set
     * @return \c true plugin is of interest; \c false otherwise
     */

    protected static boolean ofInterest( Plugin plugin, Set<Plugin> plugins, boolean allowDeny ) {
        if ( !plugin.isEnabled() )
            return false;

        return allowDeny ^ plugins.contains( plugin );
    }
}
 类似资料:
  • 当我试图在我的bukkit服务器中加载插件时,我遇到了NullPointerExc的问题,但在Eclipse中没有错误。我的插件是Rush插件,很多东西没有使用/丢失。错误是这样的: 以下是我的代码:

  • 我正试着做一个插件,你输入命令/设置,它就切换布尔设置。如果set是真的,我希望当玩家加入时,它对他们说“嗨”,但如果set是假的,它什么也不做。(顺便说一句,我是唯一可以使用这个命令的人)。我试着创建两个类,一个是主类,另一个是侦听器类,但是我不能从侦听器类访问布尔值,所以我试着把它们都放在一个类中。当使用我提供的代码时,除了PlayerJoinEvent之外,一切都正常。我要么需要解决如何从另

  • 我正在尝试恢复一个插件,但发现了一个错误: 我做的一切都是对的!plugin.yml外部罐子和出口,但仍然不起作用。知道为什么吗?

  • 问题内容: 有什么可用于Eclipse的插件,可以显示类依赖关系的图形视图吗? 问题答案: Classycle 可能是一个好的开始(至少对于类之间的静态依赖关系) (尽管我发现它们的图形有些复杂,但是:CDA-类相关性分析器是一个外部工具,但是生成的可读性更强)

  • 我正在尝试学习如何编写minecraft插件,但是我在开始时遇到了一点问题,当我尝试运行导出的插件时,我得到一个主类未找到错误。我已经尝试移动plugin.yml文件,检查所有部分都导出和重命名类等无济于事。也阅读了现有的问题。任何帮助都将不胜感激。 主要: helloCommand: 插件。yml: 控制台错误: Pic of Package Explorer编辑前一个问题,更正建议但错误仍然存