在我的bukkit/spigot插件中有一个自定义的事件,它扩展了PlayerInteractEvent,它试图在玩家周围的附近区域打开箱子。
目前,代码使用此事件来确保没有其他插件(例如,悲伤预防)对象能够打开播放器的胸腔。如果玩家可以打开箱子,我的插件将尝试将物品存放到箱子中。如果某个插件(理想情况下)或类(作为变通方法)调用了setCancelled(),我会忽略它
从this question中我可以看到,为了获得我可以使用的类
String callerClassName = new Exception().getStackTrace()[1].getClassName();
String calleeClassName = new Exception().getStackTrace()[0].getClassName();来获取类名。或者,我可以在此调用周围使用一些东西:
StackTraceElement[] stElements = Thread.currentThread().getStackTrace();然而,所有关于这个问题的评论都表明,除了这个正在做的事情之外,可能还有更好的方法来做这件事。
Bukkit有没有更好的方法来做到这一点?
作为参考,这是我的自定义播放器交互事件的全部内容:
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 }发布于 2017-03-04 05:19:53
问得好!目前,让我忽略引发这个问题的原因。Bukkit不会“发布”用于确定事件取消来源的方法。然而,您“评估”事件的方法是正确的。
正如您已经知道或怀疑的那样,使用堆栈跟踪不是一个好的解决方案。它们生成和描述可能不一定保证保持不变的特定于实现的细节的成本相对较高。一种更好的方法是模仿Bukkit在调用callEvent()时使用的事件触发过程。
虽然Bukkit API不能保证事件触发过程的实现,但它已经稳定了很多年,并且没有太大的变化。这是我们过去5年的工作,当callEvent()被拆分成callEvent()/fireEvent()时,只需要进行一次小的重构。
我希望我能给你整个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 );
}
}发布于 2017-03-03 14:08:11
我建议使用优先级
优先级按以下顺序排列:
如果将事件优先级设置为HIGHEST或MONITOR,则事件将在所有其他优先级都已侦听之后侦听该事件。例如,即使另一个插件试图取消它,你仍然可以监听事件。
注意:不建议更改具有MONITOR优先级的事件的结果。它应该只用于监控。
更改事件优先级(默认值:正常)
@EventHandler (priority = EventPriority.HIGHEST)
public void onEvent(Event e) {
}如果你想要其他插件在决赛中在你的插件之后运行-例如,如果你想让World Edit比你的插件“更强”,则将优先级设置为LOW或LOWEST。如果你想让你的插件拥有最终决定权,那就提高优先级。
编辑@
如果你想在没有优先级的情况下做这件事,并且实际上需要识别取消插件,Comprehenix from bukkit forums have a solution for you。请记住,这是,而不是推荐的
如何操作的示例:
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 here
https://stackoverflow.com/questions/42570796
复制相似问题