Solved Figuring out if the player is rightclicking/Figuring out when the player stops rightclicking

Discussion in 'Plugin Development' started by MaxBKAC, Oct 2, 2021.

  1. Offline

    MaxBKAC

    Code:
    Code:
      private HashMap<String, Long> playerClicks = new HashMap<String, Long>();
        boolean isPlayerSpamming = false;
        boolean playerSpammingBool;
        Recoded plugin;
        public AListener(Recoded plugin) {
            this.plugin = plugin;
        }
        @EventHandler
        public void onRightClick(PlayerInteractEvent event) {
            Player player = event.getPlayer();
            if ((event.getAction().equals(Action.RIGHT_CLICK_AIR)) || (event.getAction().equals(Action.RIGHT_CLICK_BLOCK))) {
                if(!playerClicks.containsKey(event.getPlayer().getName())) {
                    if(playerClicks.isEmpty()) {
                        playerClicks.put(event.getPlayer().getName(), System.currentTimeMillis()); // if Hashmap doesnt contain player click, Add playerclick+time of the click
                        return;
                    }
                }
                if (playerClicks.containsKey(event.getPlayer().getName())) {
                    playerSpammingBool = isPlayerSpammingFunc(player,                playerClicks.get(player.getName()));
                    if (playerSpammingBool == false) {
                        player.chat(ChatColor.RED + "You stopped spamming");
                    } else {
                        player.chat(ChatColor.BLUE + "You are Spamming");
                    }
                    playerClicks.remove(player.getName());
    
                }
            }
        }
        public Boolean isPlayerSpammingFunc(Player player, Long clicktime) {
            Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
                @Override
                public void run() {
                        Long timeTest = System.currentTimeMillis();
                        long testlong = timeTest - clicktime;
                        if ((testlong > 500)) {
                            isPlayerSpamming = false;
                            player.chat(Long.toString(testlong));
                        } else {
                            isPlayerSpamming = true;
                        }
                    }
            }, 40L);
            return isPlayerSpamming;
        }
    }
    
    My goal:
    My goal is to make an ability that acts as some sort of necromancer sword. It would spawn minions that attacks for the player. The minions would only be alive/attacking while the player is rightclicking. If the player stops rightclicking the minions would despawn.

    The FULL Error log: No error

    What you have tried:
    The code above is what I have tried.
    My idea on figuring out if a player is rightclicking is by creating a hashmap that contains the player name and the time of the click. Afterwards it checks using the isPlayerSpammingFunc if after a few ticks the current time - time of click is below a certain time then it would return true and the player would be considered as spamming right click else it would return false.

    I have also tried if (p.isblocking) and it doesn't seem to work maybe because I am in 1.17 and you cant really block anymore other than with shields.
    I have tried using the animations event but only "Arm_Swing" seems to be returned which doesnt help me


    This is my first thread so if i forgot to give you needed information just ask. Im not exactly sure what I need to write.
    Thank you
     
    Last edited by a moderator: Oct 4, 2021
  2. Offline

    davidclue

    Alright so first off don't store in your map player names (Strings) instead store the player's UUID and next don't use your playerSpammingFunc method instead just store with the player's UUID an integer instead of long because that's overkill. Use the map as a cooldown mechanic in seconds so have a CompletableFuture task (or if there won't be many players you can run it on the main thread) a simple iterator to iterate your map and decrease the value by 1 for every entry every second and remove entries that are equal to zero.
     
  3. Offline

    MaxBKAC

    Thank you for your reply, I had a few questions tho.
    Why do i need a cooldown in seconds to do this, wouldnt a cooldown in seconds make this faulty since my goal is to check if a player is spamming and spamming right clicking will make the player click more than once per second.
    I changed my hashmap to use UUID's but im not exactly sure what to make the Integer = to, would it be (System.currentTimeInMillis() / 1000) (for seconds) ?

    When you say
    What do you mean by value, would value be the current time? I'm not sure I understand.
    Thank you for your time.
     
  4. Offline

    rudraksha007

    i would suggest :
    make two hashmaps, store current time in milis (when he clicked) with player UUID (say its name "Clicked") in one and click start time in other (say its name be "lastClick"), then do: if(currentTimeInMillis()-Clicked.get(player.getUniqueId())<yourLimitToEndSpam)..... this will tell you that player started spam clicking... know when your runnable runs, check how much the current time is greater than what you saved in lastClick map && also in the clicked hashmap... Now what u want is currentTime-clickStart is as high as possible and currentTime-lastClick is as low as possible...
    this should be fine
     
  5. Offline

    davidclue

    @MaxBKAC Alright if you want that level of precision then just use an integer for ticks and in a repeating task cycle through your hashmap and decrease every entries value by 1 every tick and remove the entries that have a value of 0. This will let you control with precision on how often they can click for it to be considered spamming, do not use System.currentTimeInMillis() for this, I don't think you would ever need to use that for a bukkit plugin because bukkit has schedulers for this instead that go by in-game ticks.
     
  6. Offline

    MaxBKAC

    What is the value of an entry tho. What am I decreasing by 1 in my loop?

    For example in code my question would be
    Following your first instructions you said to make a hashmap that is = to <UUID, Int>
    and then after that if i understood correctly you would make an iterator and decrease it by one?
    Like this:
    for (UUID e: hashmap.keySet()) {
    hashmap - 1
    if hashmap == 0 //remove
    }
    My question would be what is the int = to
    When I initialize my hashmap like this hashmap.put(player.getUniqueID(), x); What is x

    My goal is to check if a player is holding down right click. So
    if (player is spamming right click){
    // do stuff
    }else {
    // do other stuff
    }

    Would it be possible to create a timer that starts the first time I click and is reset to 0 each click and then all i would have to check is if the timer reaches a certain number the player wouldnt seen as holding down right click anymore?
     
  7. Offline

    davidclue

    @MaxBKAC
    Code:
    private ConcurrentMap<UUID,Integer> map = new ConcurrentHashMap<>();
    
    Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, new Runnable() {
        @Override
        public void run() {
            Iterator<Entry<UUID,Integer>> iterator = map.entrySet().iterator();
            while (iterator.hasNext()) {
                Entry<UUID,Integer> entry = iterator.next();
                if (entry.getValue() > 0)
                    map.replace(entry.getKey(), entry.getValue() - 1);
                else
                    iterator.remove();
            }
        }
    }, 0L, 1L);
    This will cycle through your map and decrease every entry's value by 1 and remove those that have a value of 0. So if you add a player with a value of 20 they will have a cooldown of 1 second before they are removed from the map (20 ticks is 1 second). Since this is running on a separate thread you will have no performance impact. If you want to check if they are spamming just use
    Code:
    if (map.contains(player.getUniqueID())) {
        //they are spamming
    } else {
        //they are not spamming
    }
     
  8. Offline

    MaxBKAC

    Thank you for your help, I tested your code and all tho it might have(probaby was) been a mistake on my part it didnt work as intended.

    I finally figured it out tho(I think):
    Code:
     
    if (!playerLastClick.containsKey(player.getUniqueId())) {
                            new BukkitRunnable() {
                            @Override
                            public void run() {
                                timer++;
                                if (!(timer > 5)) {
                                    Bukkit.broadcastMessage("player is spamming");
                                } else {
                                    Bukkit.broadcastMessage("player is not spamming");
                                    playerLastClick.remove(player.getUniqueId());
                                    cancel();
                                    timer = 0;
                                }
    
                            }
                        }.runTaskTimerAsynchronously(plugin, 0, 1);
                    }
                    playerLastClick.put(player.getUniqueId(), 1);
                    if (playerLastClick.containsKey(player.getUniqueId())) {
                        timer=0;
                    }
    
    This does what I want perfectly but I dont know how bad this is when it comes to performance. If I read the docs on runnables correctly though using runTaskTimerAsynchronously would make the task be handled in another thread so the main thread wouldn't be affected so this shouldn't affect performance I think.

    One problem with my code though is that a player doesnt necessarily have to be right clicking in order to be seen as "Spamming". They could just be tapping right click to reset the timer variable.

    Thank you for your help
     
  9. Offline

    davidclue

    @MaxBKAC
    Run this on the main thread, if you are doing this little amount for 5 ticks then it will just be worse on performance to be using separate threads for this, this will have zero performance impact on the main thread. Use async tasks for things that can be done thread-safe and ones that do big operations like reading chunks or lists that have hundreds of entries.
     

Share This Page