使用计时器删除 ListenerAdapter

remove ListenerAdapter with a timer

所以我有以下问题,我想在文本频道上制作一个迷你游戏,问题是,我想创建某种超时,这样人们就不会创建多个 "listenerAdapter" 实例只会使机器人超载。 我用来加载我的游戏事件的命令(ListenerAdapter 如下)。

@Override
public void handle(List<String> args, GuildMessageReceivedEvent event) {
    // TODO Auto-generated method stub

    TextChannel channel = event.getChannel();

    channel.sendMessage("please type \"joingame\" to join! ").queue();
    event.getJDA().addEventListener(new MinigameEvent(channel, event.getAuthor(), event));
}

然后,我用来加载播放器的代码如下:

public class MinigameEvent extends ListenerAdapter {

      private final long channelId, authorId;
      private final int players=3;
      private ArraySet<User> users;
      private String textMsg;
      private Message target;
      private GuildMessageReceivedEvent outTimerEvent;
      private boolean cancelEvent;

      public MinigameEvent(MessageChannel channel, User author, GuildMessageReceivedEvent outTimerEvent) {
          this.channelId = channel.getIdLong();
          this.authorId = author.getIdLong();
          this.outTimerEvent=outTimerEvent;
          cancelEvent=false;
          this.timeOut(channel);
          users=new ArraySet<User>();
          users.add(author);
          textMsg=("registered users: "+author.getName());
          channel.sendMessage(textMsg).queue((new Consumer<Message>()
          {
              @Override
              public void accept(Message t)
              {
                  target = t;    
               }
        }));    
    }

    @Override
    public void onMessageReceived(MessageReceivedEvent event) {

        if(event.getAuthor().isBot()) {
            return;
        }
        //not respond on other channels
        if (event.getChannel().getIdLong() != channelId) {
            return; 
        }
        MessageChannel channel = event.getChannel();
        String content = event.getMessage().getContentRaw();
        if(content.equalsIgnoreCase("joingame")) {

             users.add(event.getAuthor());
             textMsg=textMsg+", "+event.getAuthor().getName();
             target.editMessage(textMsg).queue();
             if(users.size()==players) {
                 event.getChannel().sendMessage("starting").queue();
                 event.getJDA().removeEventListener(this); 
             }
        }

        if(content.equalsIgnoreCase("cancel") && event.getAuthor().getIdLong()==authorId) {
             cancelEvent=true;
             event.getJDA().removeEventListener(this); 
             event.getChannel().sendMessage("this game has been canceled").queue();
        }   
    }

        private void timeOut(MessageChannel channel) {

            Timer timer = new Timer();



            TimerTask cooldown = new TimerTask() {

                @Override
                public void run() {

                    if(cancelEvent) {
                        return;
                    }

                    if(users.size()<players) {

                         outTimerEvent.getJDA().removeEventListener(this); 

                             try {
                                destroyEvent();
                            } catch (Throwable e) {
                                // TODO Auto-generated catch block
                                e.printStackTrace();
                            }

                         channel.sendMessage("not enough players, the game has been cancelled").queue();
                    }else {
                        return;
                    }


                }


            };

            timer.schedule(cooldown, 10000L);
        }

        private void destroyEvent() throws Throwable {
            this.finalize();
        }
}

当我达到 3 人时,侦听器适配器会按预期停止工作,当事件的作者(使用 !minigame 命令的人)键入取消时也是如此。但是当计时器关闭时,它会发送消息表明游戏已被取消,但侦听器适配器仍然 运行,如果有人尝试加入,它会允许他这样做。

我目前使用 finalize 方法解决了这个问题,但我认为你可以做类似 event.getJDA().removeEventListener(this); 的事情;

您的问题是您的 this 指的是最近的 class 声明。在这种情况下,这是由您的 new TimeTask() { ... 创建的匿名 class。要引用实际注册为侦听器的外部 class,您必须改用 MinigameEvent.this

Read More

我强烈建议使用没有此问题的 lambda 表达式。另一件需要注意的事情是你使用定时器会导致线程泄漏,因为它们永远不会被你的代码关闭(How to cleanup a timer). Even better would be to use a single ScheduledExecutorService 你应该重新使用它来安排你需要的一切而不是为每个任务创建一个新的。一旦您的进程结束,它就可以关闭(就像 JDA 中的 onShutdown 事件,它在调用 shutdown() 时触发)。