Effective way to "Freeze" a player? (no movement)

Discussion in 'Plugin Development' started by Fishrock123, Dec 15, 2011.

Thread Status:
Not open for further replies.
  1. Offline

    Fishrock123

    The person who requested a plugin I am working on, DecoyBlocks, wants me to be able to freeze players in place. Is this even possible? Cancelling player move events does... bad stuff.
     
    GrandmaJam likes this.
  2. Offline

    AbdulAbdalla

    There is no "clean" solution i think. My idea is teleporting the player to the same position every second.
     
  3. Offline

    Chiller

    Now that is VERY theoretical!
     
    GrandmaJam and hintss like this.
  4. Offline

    Trc202

    Cancel the on Player Move event. It will seem like lag to them though, so you might want to send a message to let them know they are not allowed to move. (So they won't scream lag)
     
  5. Offline

    Kierrow

    I general, Spout can be of great assistance here.
    You should implement, for SpoutCraft users, a general movement freeze. That's clean.
    Though for non-SpoutCraft users, just use the method that appears the cleanest.
    Btw, Spout (without the client mod) has a "smooth teleportation" implemented, maybe you should
    have a look at it.
    Another idea would be to set the player's velocity to nothing (0, 0, 0) whenever a PlayerMove event is called.
    And one more thing, if you use the onPlayerMove event method, you should set it's priority to Lowest, as that
    gets the first callback (Check here in case you didn't know). That might (I'm not sure) have a better effect.

    Hope this helps you.
     
    Fishrock123 likes this.
  6. Offline

    Fishrock123

    Well...

    Code:java
    1. public void onPlayerMove(PlayerMoveEvent e) {
    2. e.getPlayer().setVelocity(new Vector().zero());
    3. }


    ...Doesn't exactly freeze the player, but does give a nice slowdown effect.
     
  7. Offline

    Kierrow

    @Fishrock123

    But it's still something useful for a lot of other things.:cool:

    Thank you for correcting me. You only learn by making mistakes.
     
  8. Offline

    Waffletastic

    Just have onPlayerMove, teleport him to the location he is at.
     
  9. Offline

    nisovin

    Or... you could do something simpler... like spawn glass blocks around the player.
     
    GrandmaJam and Bone008 like this.
  10. Offline

    CptSausage

    Check onPlayerMove and if the player leaves the block, port him back into the middle.
     
    GrandmaJam and Fishrock123 like this.
  11. Offline

    tomjw64

    This is probably the best solution I would say
     
  12. Offline

    fromgate

    Not the best. If player move near the fence (and some other blocks) he will be teleported into the fence.
     
  13. Offline

    Chipmunk9998

    One way you could do it is save the player's location in a hashmap, and whenever they move, teleport them to that location.
     
    GrandmaJam likes this.
  14. Offline

    ItsHarry

    You would rather cancel the PlayerMoveEvent instead of teleporting them
     
  15. Cancelling the movement is the easiest and I belive the only best way... teleporting players and hashmaps are completly pointless.
    You can also apply some potion effects as well to limit the movement, like slowness.
     
  16. Offline

    MuisYa

    There is no possible way of stopping a player's movement...
    The only thing you can do is cancel it when he tries to move.

    Code:
    @EventHandler
    public void onPlayerMove(PlayerMoveEvent event) {
        if (event.getPlayer().getName().equalsIgnoreCase("Frozenplayer") {
            event.setCancelled(true);
        }
    }
     
  17. Offline

    tomjw64

    I've actually found that I like teleporting the player back to the center of the block rather than cancelling the move event. It's a lot less glitchy and looks a lot less like lag, more like they are being forced to stay on a single block, which they are.
    Code:java
    1.  
    2. @EventHandler(priority=EventPriority.NORMAL)
    3. public void move(PlayerMoveEvent move)
    4. {
    5. Location from=move.getFrom();
    6. Location to=move.getTo();
    7. double x=Math.floor(from.getX());
    8. double z=Math.floor(from.getZ());
    9. if(Math.floor(to.getX())!=x||Math.floor(to.getZ())!=z)
    10. {
    11. x+=.5;
    12. z+=.5;
    13. move.getPlayer().teleport(new Location(from.getWorld(),x,from.getY(),z,from.getYaw(),from.getPitch()));
    14. }
    15. }
    16.  
     
    GrandmaJam likes this.
  18. Offline

    Sheepii

    Recording PlayerMoveEvent is probably the most lag enducing event that you can do. It can bring your server from 20 ticks to 5 ticks in one single event. Hope you have a good server box.
     
  19. Offline

    Fishrock123

    Yep. The only decent way of doing this is continuously teleporting the player until Mojang gives us a "freeze" option in the upcoming API.
     
  20. Offline

    TheTrixsta

    not very effective

    or just use... e.setTo(e.getFrom()) //something like this dont know the exact method off the top of my head

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 22, 2016
  21. Offline

    tomjw64

    oh, that would be so helpful...

    Also, I really do doubt this. When you think about it, Minecraft and Bukkit have to take things into account and process things when the player moves too. And while it is probably the most lag causing event, I doubt that listening to one PlayerMoveEvent is going to quadruple your server lag.

    But hey! I could be wrong! I'm no java expert.

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 22, 2016
  22. Offline

    Malikk

    It's easily the most server intensive event. But like anything, it has a lot more to do with what you actually do when you listen to it.
     
    tomjw64 likes this.
  23. Offline

    Sheepii

    Think of it this way. If you have say, 20 people online. Movement is counted by head positioning(everytime you move your cursor), body movement(everytime you press w,a,s, or d) and vertical movement(everytime you jump or dig yourself into a hole) 20 people moving at the same time. It will record 20ticks(20(m+h)). Meaning everytime some guy on your server moves even if the event is cancelled on checking something, it is recorded and there is no way to make it not do that with an EventHandler. Coming from my server which has 100-150 people on daily, an PlayerMoveEvent would easily outprocess that many ticks off my server.

    Try it this way, on command(or action), tp them up .5 z(half a block) Put an entity that they can interact with under them(pressureplate), and make it a PlayerInteractEvent, If they are interacting with the object, and are on said hashmap of people that are frozen teleport them back(up .5x and previous location of where the interaction was). That way you don't have to record everytime a player walks.
    ^ more coding, less lag.
    Or this, you could save the location when "frozen" and every 10 ticks(half a second) it will locate that positioning, and teleport them back to it, that way it doesn't use PlayerMoveEvent
    ^less coding, more lag(it still has to call from the hashmap every half a second which can be a pain in the ass unless you have a predetermined time on how long they're frozen for)
    Or you could save the players location on "freeze", create blocks that are not normally seen(for example diamond_ore) encasing the player, teleport them back into the middle, make it so the entities look as though they are air, and make the blocks go away during a set delay.
    ^Much more coding, complication, and possibly buggy, much less lag.

    These are, in my opinion, better ways to make a freeze
     
  24. Offline

    AmoebaMan

    The server I code for used to run a plugin called PreciousStones that would cause us intense lag due to the presence of Snitch Bricks, a feature that checked onPlayerMove if a player was within 5 blocks of all the registered Snitch Bricks. Obviously this caused us a bit of lag, but I think that is was mostly because it had to iterate through the list of Snitch Bricks and calculate distance for each one individually, then save a timestamp if they were close enough. This kind of calculation done so often is, obviously, lag-inducing.

    However, the code to cancel a player movement would be extremely simple and extremely quick. Simply use a HashSet (the time for finding an element is short and constant) and store all players who are frozen. A Map is not necessary, just use contains(player) on the Set. Then if the HashSet contains the player, cancel the event.
     
  25. I'm sorry but you don't know what you are talking about. Do you realize what the server itself performs whenever your "deadly PlayerMoveEvent" occurs? It's that method (oh, and you have to add all the networking processing it takes to receive and parse the packet in the first place, even though that's partially threaded): Click me

    For reference, that's about 250 lines of code - every time any of your 150 players moves a finger. And now you tell me that it will start lagging out your server if one (or even 10) plugins just start to hook the event (that is thrown anyway)?!

    Yes, of course the event is thrown a gazillion of times on a big server - but so what? There is a lot more to process each time that event is called. The case when it starts impacting performance only happens when some crappy inexperienced plugin author managed to handle the event wrongly.
    And that's the key point: Using the event is not bad, but one needs to know how to use it correctly.

    " ... there's no way to make it not do that with an EventHandler"
    There is, you can return from the method when the event isn't of interest for you as soon as possible. Then, it'll cause no noticable impact on the server. For example, most of the plugins that need to use the event only need to know when the player moved from one block to another.

    So you can cut it down to that already:
    Code (open)

    Code:
    if (event.isCancelled())
                return;
     
            Location locFrom = event.getFrom();
            Location locTo = event.getTo();
     
            if (locFrom.getBlockX() != locTo.getBlockX() || locFrom.getBlockY() != locTo.getBlockY() || locFrom.getBlockZ() != locTo.getBlockZ()) {
                // do your stuff here
            }

    Now, even when the calculations you have to do are a bit heavy-weight, the amount of times they are done is heavily reduced by that.

    Next restriction a plugin can make: Only listen for certain players.
    Code (open)
    if(!myHashSetWithWatchedPlayers.contains(event.getPlayer().getName())
    return;

    A contains-check on a HashSet<String> is extremely fast, you can easily do that 1000 times a second without any impact (and at this point, 1000 times/sec would already be on a HUGE server where all players happen to be moving at the same time).

    So tell me again: Will that drop your tickrate more than the checks and operations done by the server (mentioned at the beginning of this post)?
    If your CPU manages to handle 150 online players, that won't change even with 10 (well written) plugins hooking PlayerMoveEvent.
     
    tomjw64 and ferrybig like this.
  26. Offline

    fromgate

    I've created a ticket adding methods to freeze player: https://bukkit.atlassian.net/browse/BUKKIT-1765

    It was created today and resolved today. Resolving result was: "Won't Fix [ 2 ]"

    Amaranth said "If the current events are lacking some functionality you think this will provide the correct solution is to file a bug for those or fix those. The API you propose here will never get in to Bukkit as its a very specific feature that plugins can easily offer themselves."

    But I think, that it not possible yet freeze player without allow him to move, and sometimes (if he was freezed near door, glass, fence gate he can move through that block).

    I have tried:

    1. Cancel event. It works but player can run about 3-10 blocks than return to his original position. If he freezed near the private door (event PlayerInteractEvent is cancelled while hi trying to open the door) he can glitch through the door by walking, right clicking the door and holding a shift key).

    Code:java
    1. @EventHandler(priority=EventPriority.MONITOR, ignoreCancelled = true)
    2. public void freezePlayerMove (PlayerMoveEvent event){
    3. if (CheckFreezed(event.getPlayer())) event.setCancelled(true);
    4. }
    5.  



    2. Set movement target location to the source location. Works like previous example.

    Code:java
    1. @EventHandler(priority=EventPriority.MONITOR, ignoreCancelled = true)
    2. public void freezePlayerMove (PlayerMoveEvent event){
    3. if (CheckFreezed(event.getPlayer())) event.setTo(event.getFrom());
    4. }
    5.  


    3. Teleporting player to the "From" location. Works better: player can not running too much, but he still can move trough the closed doors.
    Code:java
    1. @EventHandler(priority=EventPriority.MONITOR, ignoreCancelled = true)
    2. public void freezePlayerMove (PlayerMoveEvent event){
    3. if (CheckFreezed(event.getPlayer())) {
    4. event.getPlayer.teleport(event.getFrom());
    5. event.setCancelled(true);
    6. }
    7.  


    4. Teleporting player to the "From" location with a delay.Works as No.3. I've tryed to delay values till 1 to 5 ticks - nothing changes.
    Code:java
    1. @EventHandler(priority=EventPriority.MONITOR, ignoreCancelled = true)
    2. public void freezePlayerMove (PlayerMoveEvent event){
    3. if (CheckFreezed(event.getPlayer())) {
    4. final Player p = event.getPlayuer();
    5. final Location tloc=event.getFrom();
    6. event.setCancelled(true);
    7. event.setTo(tloc);
    8. pugin.getServer().getScheduler().scheduleSyncDelayedTask(plg, new Runnable(){
    9. public void run(){
    10. tp.teleport(tloc);
    11. }
    12. },2L);
    13. }
    14.  


    5. I've tried teleporting (including delayed teleport) player to center of block. It works, but if player locating near fence when he freezed he will teleported inside a fence, and can walk through it after he was unfreezed.
    Code:java
    1. @EventHandler(priority=EventPriority.MONITOR, ignoreCancelled = true)
    2. public void freezePlayerMove (PlayerMoveEvent event){
    3. if (CheckFreezed(event.getPlayer())) {
    4. Location loc = event.getFrom();
    5. loc.setX(loc.getBlockX()+0.5);
    6. loc.setY(loc.getBlockY());
    7. loc.setZ(loc.getBlockZ()+0.5);
    8. event.getPlayer.teleport(loc);
    9. event.setCancelled(true);
    10. }
    11.  


    Anyone have a solution how to totally freeze player?
    May be it possible using CraftBukkit API, not the Bukkit API?
     
    GrandmaJam likes this.
  27. Offline

    PandazNWafflez

    Just want to point out that there is no CraftBukkit API, Bukkit is the API for CraftBukkit that CraftBukkit implements.
     
  28. fromgate
    For clarification: You should not teleport inside your move listener. Use event.setTo(...), bukkit will do the rest for you.
    The most effective way is to use setCancelled(true), that directly sends back a correction packet to the old position for the client (as seen in the NMS code of my previous post).
    The reason it didn't work well for you was probably latency. Did you watch your tested player with another account on the server? The server-side entity shouldn't have moved at all. If you have an anti-cheat plugin like NoCheat(+), you should also not be able to interact with stuff that you normally wouldn't reach from your freezed position.

    Using PlayerMoveEvent it is actually possible to make the server-side player stay at the same position, that the client freaks out is its own fault.

    The best solution in my eyes is still that:
    Either do that for real and let your anti-cheat plugin do the rest for you, or just fake the glass blocks (keyword: Player.sendBlockChange) client-side and still go with PlayerMoveEvent. A notchian client won't be able to move out of the glass box, and a hacked one will be stopped by the event handler (we don't really have to care if hackers don't see a pretty outcome on their screen, right?).

    Oh, and in general, don't forget to listen to PlayerTeleportEvent as well.
     
    fromgate likes this.
  29. Offline

    Fishrock123

    Amaranth Isn't entirely right. Due to the nature how how minecraft handles movement input, there is no clean-cut good way to implement this until Minecraft 1.4 / the Mojang api.

    The client will keep telling the server it is at a new position no matter what you do, and the server will keep causing you to move around oddly because of that, even if you cancel every playermove event.

    The *Best* way as far as I can tell is to do this is just to run a sync task that teleports the player back to the location every tick, and simply cancel all playerinteract events with it.
     
    fromgate likes this.
  30. Offline

    fromgate

    I know about teleporting and event.setTo methods, but you know - I'm now going to try everything that could help me. If it works - I'll use it :)


    Did I understand you right: sending a block change will send a fake block information and player will see that he surrounded with fake block. But other player will not see that fake block, and still can interact with him (hit for example, move near him, etc...)?

    Oh.... I'll try it today. Thank you.
     
Thread Status:
Not open for further replies.

Share This Page