2

so I have the following problem, I want to make a minigame on a text channel, the problem is, I want to create some sort of timeout so that people don't create multiple "listenerAdapter" instances that will just overload the bot. the command I use to load my game event (ListenerAdapter is as follows).

@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));
}

then , the code I use for loading players in, is the following:

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();
        }
}

when I get to 3 people, the Listener adapter stops working as intended, also when the author of the event (the one who used the !minigame command) types cancel. but when the timer goes off, it sends the message indicating the game has been cancelled, but the listener adapter is still running, if someone tries to join after, it will allow him to do so.

I currently solved the issue by using the finalize method, but I thought that you could just do something like event.getJDA().removeEventListener(this);

Roberto Pegoraro
  • 1,313
  • 2
  • 16
  • 31
rorod8
  • 33
  • 4
  • Why can't you write it so you can just create a singleton of the Listener class. So the first time it is instantiated, it creates a static singleton. Then subsequent calls would return the same instance. – WJS Jul 30 '19 at 00:39
  • @WJS I might look into that but the thing is, it has to work for different servers, so multiple instances might be needed – rorod8 Jul 30 '19 at 00:52

1 Answers1

2

Your problem is that your this refers to the nearest class declaration. In this case this is the anonymous class created by your new TimeTask() { .... To refer to the outer class which actually is registered as the listener you have to use MinigameEvent.this instead.

Read More

I would highly recommend using a lambda expression instead which doesn't have this problem. Another thing to note is your use of timer which will result in thread leaks because they are never shutdown by your code (How to cleanup a timer). Even better would be to use a single ScheduledExecutorService which you should re-use to schedule everything you need rather than creating a new one for every task. This can then be shutdown once your process ends (like the onShutdown event in JDA which is fired when shutdown() is called).

Minn
  • 5,688
  • 2
  • 15
  • 42