Solved Detect when a player leaves the end through portal?

Discussion in 'Spigot Plugin Development' started by MartinOdum, Feb 6, 2020.

  1. Hello all, I'm looking for some assistance after countless google searches and searching through these forums aswell I haven't exactly found a solution.

    Version: SPIGOT 1.15.2

    My Goal: Detect when a player has exited the end and is returning to the overworld using the gateway in the end. I want to intercept the event and change the location the player will be teleported to depending on which end world they are exiting. I plan to have multiple end worlds and the default behavior is that all of them send the player back to the main overworld when leaving the end, I need to send them to different worlds depending on which end world they just left

    My attempts:
    1)
    I attempted the obvious and used the PlayerPortalEvent however I found out eventually that the event does not fire when the player enters the return to overworld end portal, it will fire for nether portals and end portals FROM the overworld but NOT the end portal destined TO the overworld

    2) PlayerTeleportEvent is also not called in this situation so no luck there

    3) Lastly I also attempted using the EntityPortalEnterEvent, I had some luck here in the sense that the event does fire when I jump into the portal so I tried the following code but I got some serious wonky results, it worked the very first time I ran the code but after that it never worked again, it did things like teleport somewhere else in the same end world instead of the world I specified, I basically don't think this event is the solution

    Code (Java):
    @EventHandler
        public void tpTester(EntityPortalEnterEvent event){
            if (event.getLocation().getWorld().equals(Bukkit.getWorld("bpseason1_the_end"))){
                if(event.getLocation().getWorld().getEnvironment().equals(World.Environment.THE_END)){
                    event.getEntity().teleport(Bukkit.getWorld("bpseason1").getSpawnLocation());
                    Bukkit.broadcastMessage("Debug: This event fired and code was executed");
                }
            }
        }
    Notes Worth Mentioning:
    I found this while google searching and although it was being discussed for 1.14, what the report describes is what I am dealing with now, I'm not sure what to make of md_5's response in that report, I think my attempt #3 was what he is saying to do but honestly I'm a bit confused from the response.

    Reference: https://hub.spigotmc.org/jira/browse/SPIGOT-4770?workflowName=Jira+Fixed&stepId=4

    Any help in achieving my goal is appreciated thanks!
     
  2. Not gonna lie I feel silly, I can't believe I never thought of testing this. I gave it a shot and was able to achieve my goal, my only concern is that I can't distinguish if the player left the world through the portal or if he left the world due to something like a teleport command, any ideas if there is an easy fix for this, I have a rough idea on how to approach the issue but its a bit more complex, just figured I'd ask before going that route.

    Edit: In case anyone in the future has the same issue this is the code I quickly tested and it works
    Code (Java):
        @EventHandler
        public void tptest(PlayerChangedWorldEvent event){
            if(event.getFrom().getName().equals("bpseason1_the_end")){
                event.getPlayer().teleport(Bukkit.getWorld("bpseason1").getSpawnLocation());
            }
        }
     
  3. You said that when using PlayerPortalEnterEvent the event had been registered, try using that event to call your other method that you have worked on.
    So you can distinguish when the player used a command or the portal
     
    • Like Like x 1
  4. I'm not entirely sure what you meant but I did figure out a way to do it, it may not be the best way to do it but it seems to do the job.

    Code (Java):
        public HashMap <String, Integer> enteredendportal = new HashMap<>();
       
        @EventHandler
        public void eportal(EntityPortalEnterEvent event){
            if (event.getLocation().getWorld().equals(Bukkit.getWorld("bpseason1_the_end"))){
                if(event.getEntity() instanceof Player){
                    Integer unixTime = (int) (System.currentTimeMillis() / 1000L);
                    enteredendportal.put(event.getEntity().getUniqueId() + event.getLocation().getWorld().getName(), unixTime);
                }
            }
        }
        @EventHandler
        public void tptest(PlayerChangedWorldEvent event){
            if(event.getFrom().getName().equals("bpseason1_the_end")){
                if(enteredendportal.get(event.getPlayer().getUniqueId().toString() + "bpseason1_the_end")!=null){
                    long Unix = enteredendportal.get(event.getPlayer().getUniqueId().toString() + "bpseason1_the_end");
                    Integer CurrentUnix = (int) (System.currentTimeMillis() / 1000L);
                    if(Unix + 2 >= CurrentUnix){
                        event.getPlayer().teleport(Bukkit.getWorld("bpseason1").getSpawnLocation());
                    }
                }
            }
        }
    Basically I'm using unix time and a hashmap to record and be able to tell if the player has entered a portal in the last two seconds when the player leaves an end world. If he has entered a portal in the past two seconds then that means he probably used the return portal and he is teleported to the appropriate world. I believe this should do the job, the code is quick test code so I'll have to make sure its foolproof and whatnot. I appreciate the help anyway even though I went a different route.
     
  5. I would probably change your approach just slightly. Currently, every time a player goes in a portal, their UUID is added to the HashMap with the time stamp. You could slim down your code a little and make the check a lot easier if you change the HashMap to an ArrayList<UUID> and when they enter the end portal, add the player's uuid to the list and start a new BukkitRunnable that simply removes their UUID after 2 seconds. Then in the ChangedWorldEvent all you have to do is check if the list contains the player's UUID and you don't have to worry about checking time at all.

    What you have should work just fine, this is just my thoughts on the subject.
     
    • Agree Agree x 1
  6. Oh I like that idea a lot! Like you said it is much easier to just make a quick bukkit runnable rather then doing the whole unix thing, thanks for the suggestion!
     
    • Like Like x 1