Package org.spout.vanilla.protocol.handler.player.pos

Source Code of org.spout.vanilla.protocol.handler.player.pos.PlayerPositionHandler$PositionTracker

/*
* This file is part of Vanilla.
*
* Copyright (c) 2011 Spout LLC <http://www.spout.org/>
* Vanilla is licensed under the Spout License Version 1.
*
* Vanilla is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option)
* any later version.
*
* In addition, 180 days after any changes are published, you can use the
* software, incorporating those changes, under the terms of the MIT license,
* as described in the Spout License Version 1.
*
* Vanilla is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
* more details.
*
* You should have received a copy of the GNU Lesser General Public License,
* the MIT license and the Spout License Version 1 along with this program.
* If not, see <http://www.gnu.org/licenses/> for the GNU Lesser General Public
* License and see <http://spout.in/licensev1> for the full license, including
* the MIT license.
*/
package org.spout.vanilla.protocol.handler.player.pos;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import gnu.trove.iterator.TDoubleIterator;
import gnu.trove.iterator.TLongIterator;
import gnu.trove.list.TDoubleList;
import gnu.trove.list.TLongList;
import gnu.trove.list.linked.TDoubleLinkedList;
import gnu.trove.list.linked.TLongLinkedList;

import org.spout.api.entity.Player;
import org.spout.api.geo.World;
import org.spout.api.geo.discrete.Point;
import org.spout.api.geo.discrete.Transform;
import org.spout.api.material.BlockMaterial;
import org.spout.api.material.block.BlockFace;
import org.spout.api.protocol.ClientSession;
import org.spout.api.protocol.MessageHandler;
import org.spout.api.protocol.ServerSession;
import org.spout.api.protocol.Session;
import org.spout.api.protocol.reposition.RepositionManager;

import org.spout.vanilla.VanillaPlugin;
import org.spout.vanilla.component.entity.living.Human;
import org.spout.vanilla.component.entity.player.Ping;
import org.spout.vanilla.component.entity.player.VanillaPlayerNetworkComponent;
import org.spout.vanilla.material.VanillaBlockMaterial;
import org.spout.vanilla.protocol.msg.player.pos.PlayerPositionMessage;

public final class PlayerPositionHandler extends MessageHandler<PlayerPositionMessage> {
  long last = System.nanoTime();
  //Player movement is 0.21 apart
  //Player running is 0.27 apart
  //Player swimming is 0.14-0.11 apart
  //Player flying is 0.5+
  private final Cache<Session, PositionTracker> cache = CacheBuilder.newBuilder().expireAfterAccess(1, TimeUnit.SECONDS).build();
  private final Callable<PositionTracker> loader = new Callable<PositionTracker>() {
    public PositionTracker call() {
      return new PositionTracker();
    }
  };

  @Override
  public void handleClient(ClientSession session, PlayerPositionMessage message) {
    Player player = session.getPlayer();

    World world = session.getEngine().getDefaultWorld();
    player.getPhysics().setPosition(new Point(world, (float) message.getX(), (float) message.getY(), (float) message.getZ()));
    // TODO: player position isnt updated
    System.out.println(message.toString());
  }

  @Override
  public void handleServer(ServerSession session, PlayerPositionMessage message) {
    final Player holder = session.getPlayer();
    final RepositionManager rmInverse = holder.getNetwork().getRepositionManager().getInverse();

    Ping ping = holder.get(Ping.class);
    if (ping != null) {
      ping.refresh();
    }

    final Point rawPosition = new Point(message.getPosition(), holder.getWorld());
    final Point newPosition = rmInverse.convert(rawPosition);
    final Point position = holder.getPhysics().getPosition();

    if (!(holder.getNetwork() instanceof VanillaPlayerNetworkComponent)) {
      throw new IllegalStateException("Using Vanilla Protocol without using VanillaNetworkSynchronizer");
    }
    if (!position.equals(newPosition)) {
      final Human human = holder.get(Human.class);
      // TODO: live?
      holder.getPhysics().setTransform(new Transform(newPosition, holder.getPhysics().getRotation(), holder.getPhysics().getScale()), false);

      //Don't use client onGround value, calculate ourselves
      //MC Client is on ground if y value is whole number, or half step (e.g 55.0, or 65.5)
      float yDiff = Math.abs(newPosition.getBlockY() - newPosition.getY());
      if (yDiff > 0.4) {
        yDiff -= 0.5F; //half blocks
      }
      final BlockMaterial ground = newPosition.getBlock().translate(BlockFace.BOTTOM).getMaterial();
      final boolean onGround = yDiff < 0.01 && (ground instanceof VanillaBlockMaterial && ground.getShape() != null);
      final boolean wasOnGround = human.isOnGround();
      human.setOnGround(onGround);

      //Update falling state
      final boolean wasFalling = human.isFalling();
      if (!onGround && newPosition.getY() < position.getY()) {
        human.setFalling(true);
      } else {
        human.setFalling(false);
      }

      //Hover tracking
      if (wasOnGround && !onGround) {
        human.getData().put("position_on_ground", position);
        human.getData().put("time_in_air", holder.getWorld().getAge());
      } else if (!wasOnGround && !onGround) {
        //Changed directions
        if (wasFalling && !human.isFalling() || human.isInWater()) {
          human.getData().remove("time_in_air");
        }
        float time = human.getData().get("time_in_air", holder.getWorld().getAge());
        //hovering or still rising
        if (time + 2000 < holder.getWorld().getAge() && newPosition.getY() - position.getY() >= 0) {
          if (!human.canFly()) {
            holder.sendMessage("Hover cheating?");
          }
        }
      } else if (!wasOnGround && onGround) {
        human.getData().remove("position_on_ground");
        human.getData().remove("time_in_air");
      }

      //Movement tracking
      PositionTracker tracker = null;
      try {
        tracker = cache.get(session, loader);
      } catch (ExecutionException e) {
        throw new RuntimeException(e);
      }
      tracker.updateTracker(human, position, newPosition, message.getCreationTimestamp());

      //Debug
      /*
      final double dx = position.getX() - newPosition.getX();
      final double dy = position.getY() - newPosition.getY();
      final double dz = position.getZ() - newPosition.getZ();
      System.out.println("Player position packet statistics:");
      System.out.println("    Snapshotted position: (" + position.getX() + ", " + position.getY() + ", " + position.getZ() + ")");
      System.out.println("    New position: (" + newPosition.getX() + ", " + newPosition.getY() + ", " + newPosition.getZ() + ")");
      System.out.println("    DX: " + dx);
      System.out.println("    DY: " + dy);
      System.out.println("    DZ: " + dz);
      System.out.println("    Distance: " + position.distance(newPosition));
      System.out.println("    Time since last packet: " + (System.nanoTime() - last) / 1E6D + " ms");
      System.out.println("    Avg Distance: " + tracker.getAvgMovement());
      System.out.println("    Avg packet delta: " + (tracker.getAvgMessageTime() / 1E6D) + " ms");
      System.out.println("    Message On Ground: " + message.isOnGround());
      System.out.println("    Calculated On Ground: " + onGround);
      System.out.println("    Sneaking: " + human.isSneaking());
      System.out.println("    Sprinting: " + human.isSprinting());
      System.out.println("    Swimming: " + human.isInWater());
      System.out.println("    Ping: " + holder.get(Ping.class).getPing());*/
      last = System.nanoTime();
     
      //TODO This is way too aggressive, needs to be revised.
      //        if (tracker.isFilled()) {
      //          //Flying?
      //          if (tracker.getAvgMovement() > 0.3D && !human.canFly()) {
      //            holder.sendMessage("Flying? (Speed: " + tracker.getAvgMovement());
      //          }
      //          //Flooding packets?
      //          if (tracker.getAvgMessageTime() < 40F * 1E6F) {
      //            holder.sendMessage("Speed Hacking?");
      //          }
      //        }
    }
  }

  private static class PositionTracker {
    private final TLongList messageTimeDeltas = new TLongLinkedList();
    private final TDoubleList distanceDeltas = new TDoubleLinkedList();
    private long lastMessage = System.nanoTime();

    public boolean isFilled() {
      return messageTimeDeltas.size() >= 50;
    }

    public void updateTracker(Human human, Point prevPoint, Point newPoint, long created) {
      //Don't track updates if the last one was > 500 ms ago
      if (created - lastMessage > 500 * 1E6) {
        lastMessage = created;
      } else {
        messageTimeDeltas.add(created - lastMessage);
        distanceDeltas.add(normalizeDistance(human, prevPoint, newPoint));
        if (messageTimeDeltas.size() > 50) {
          messageTimeDeltas.removeAt(0);
        }
        if (distanceDeltas.size() > 50) {
          distanceDeltas.removeAt(0);
        }
        lastMessage = created;
      }
    }

    private double normalizeDistance(Human human, Point prevPoint, Point newPoint) {
      final float dx = prevPoint.getX() - newPoint.getX();
      final float dz = prevPoint.getZ() - newPoint.getZ();
      final float dist = (float) Math.sqrt(dx * dx + dz * dz);
      final double tpsModifier = 1D / Math.max(1F, 20F / VanillaPlugin.getInstance().getTPSMonitor().getTPS());

      if (human.isSneaking()) {
        return (dist / 0.08D) * 0.22D * tpsModifier;
      }
      if (human.isSprinting()) {
        return (dist / 0.32D) * 0.22D * tpsModifier;
      }
      return dist * tpsModifier;
    }

    public double getAvgMovement() {
      if (distanceDeltas.isEmpty()) {
        return 0;
      }
      double total = 0;
      TDoubleIterator i = distanceDeltas.iterator();
      while (i.hasNext()) {
        total += i.next();
      }
      return total / distanceDeltas.size();
    }

    public double getAvgMessageTime() {
      if (messageTimeDeltas.isEmpty()) {
        return 0;
      }
      long total = 0;
      TLongIterator i = messageTimeDeltas.iterator();
      while (i.hasNext()) {
        total += i.next();
      }
      return total / (double) messageTimeDeltas.size();
    }
  }
}
TOP

Related Classes of org.spout.vanilla.protocol.handler.player.pos.PlayerPositionHandler$PositionTracker

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.