This commit is contained in:
TheMode 2019-08-30 01:17:46 +02:00
parent 932fb6ae2b
commit d929730a61
10 changed files with 260 additions and 98 deletions

View File

@ -0,0 +1,66 @@
package fr.themode.minestom.collision;
import fr.themode.minestom.entity.Entity;
/**
* See https://wiki.vg/Entity_metadata#Mobs_2
*/
public class BoundingBox {
private Entity entity;
private float x, y, z;
public BoundingBox(Entity entity, float x, float y, float z) {
this.entity = entity;
this.x = x;
this.y = y;
this.z = z;
}
public boolean intersect(BoundingBox boundingBox) {
return (getMinX() <= boundingBox.getMaxX() && getMaxX() >= boundingBox.getMinX()) &&
(getMinY() <= boundingBox.getMaxY() && getMaxY() >= boundingBox.getMinY()) &&
(getMinZ() <= boundingBox.getMaxZ() && getMaxZ() >= boundingBox.getMinZ());
}
public BoundingBox expand(float x, float y, float z) {
return new BoundingBox(entity, this.x + x, this.y + y, this.z + z);
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public float getZ() {
return z;
}
public float getMinX() {
return entity.getPosition().getX() - (x / 2);
}
public float getMaxX() {
return entity.getPosition().getX() + (x / 2);
}
public float getMinY() {
return entity.getPosition().getY() - (y / 2);
}
public float getMaxY() {
return entity.getPosition().getY() + (y / 2);
}
public float getMinZ() {
return entity.getPosition().getZ() - (z / 2);
}
public float getMaxZ() {
return entity.getPosition().getZ() + (z / 2);
}
}

View File

@ -3,6 +3,7 @@ package fr.themode.minestom.entity;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.Main;
import fr.themode.minestom.Viewable;
import fr.themode.minestom.collision.BoundingBox;
import fr.themode.minestom.data.Data;
import fr.themode.minestom.data.DataContainer;
import fr.themode.minestom.event.Callback;
@ -11,6 +12,7 @@ import fr.themode.minestom.event.Event;
import fr.themode.minestom.instance.Chunk;
import fr.themode.minestom.instance.Instance;
import fr.themode.minestom.net.packet.server.play.*;
import fr.themode.minestom.net.player.PlayerConnection;
import fr.themode.minestom.utils.Vector;
import fr.themode.minestom.utils.*;
@ -40,6 +42,8 @@ public abstract class Entity implements Viewable, DataContainer {
protected float lastYaw, lastPitch;
private int id;
private BoundingBox boundingBox;
protected Entity vehicle;
// Velocity
// TODO gravity implementation for entity other than players
@ -86,6 +90,8 @@ public abstract class Entity implements Viewable, DataContainer {
this.uuid = UUID.randomUUID();
this.position = new Position();
setBoundingBox(0, 0, 0);
synchronized (entityById) {
entityById.put(id, this);
}
@ -103,7 +109,7 @@ public abstract class Entity implements Viewable, DataContainer {
public abstract void update();
// Called when entity a new instance is set
// Called when a new instance is set
public abstract void spawn();
public abstract boolean isOnGround();
@ -145,7 +151,9 @@ public abstract class Entity implements Viewable, DataContainer {
public void addViewer(Player player) {
this.viewers.add(player);
player.viewableEntity.add(this);
player.getPlayerConnection().sendPacket(getVelocityPacket());
PlayerConnection playerConnection = player.getPlayerConnection();
playerConnection.sendPacket(getVelocityPacket());
playerConnection.sendPacket(getPassengersPacket()); // TODO fix synchronization
}
@Override
@ -198,7 +206,7 @@ public abstract class Entity implements Viewable, DataContainer {
// Velocity
if (velocityTime != 0) {
if (time >= velocityTime) {
sendPositionSynchronization(); // Send synchronization after velocity ended
sendSynchronization(); // Send synchronization after velocity ended
resetVelocity();
} else {
if (this instanceof Player) {
@ -250,7 +258,7 @@ public abstract class Entity implements Viewable, DataContainer {
// Scheduled synchronization
if (time - lastSynchronizationTime >= synchronizationDelay) {
lastSynchronizationTime = System.currentTimeMillis();
sendPositionSynchronization();
sendSynchronization();
}
}
@ -296,6 +304,14 @@ public abstract class Entity implements Viewable, DataContainer {
return isActive;
}
public BoundingBox getBoundingBox() {
return boundingBox;
}
public void setBoundingBox(float x, float y, float z) {
this.boundingBox = new BoundingBox(this, x, y, z);
}
public Instance getInstance() {
return instance;
}
@ -350,7 +366,7 @@ public abstract class Entity implements Viewable, DataContainer {
this.passengers.add(entity);
entity.vehicle = this;
sendPassengersPacket();
sendPacketToViewersAndSelf(getPassengersPacket());
}
public void removePassenger(Entity entity) {
@ -360,7 +376,7 @@ public abstract class Entity implements Viewable, DataContainer {
return;
this.passengers.remove(entity);
entity.vehicle = null;
sendPassengersPacket();
sendPacketToViewersAndSelf(getPassengersPacket());
}
public boolean hasPassenger() {
@ -371,7 +387,7 @@ public abstract class Entity implements Viewable, DataContainer {
return Collections.unmodifiableSet(passengers);
}
protected void sendPassengersPacket() {
protected SetPassengersPacket getPassengersPacket() {
SetPassengersPacket passengersPacket = new SetPassengersPacket();
passengersPacket.vehicleEntityId = getEntityId();
@ -382,7 +398,7 @@ public abstract class Entity implements Viewable, DataContainer {
}
passengersPacket.passengersId = passengers;
sendPacketToViewersAndSelf(passengersPacket);
return passengersPacket;
}
public void triggerStatus(byte status) {
@ -427,6 +443,10 @@ public abstract class Entity implements Viewable, DataContainer {
position.setY(y);
position.setZ(z);
for (Entity passenger : getPassengers()) {
passenger.position = getPosition().clone();
}
Instance instance = getInstance();
if (instance != null) {
Chunk lastChunk = instance.getChunkAt(lastX, lastZ);
@ -435,52 +455,15 @@ public abstract class Entity implements Viewable, DataContainer {
synchronized (instance) {
instance.removeEntityFromChunk(this, lastChunk);
instance.addEntityToChunk(this, newChunk);
for (Entity passenger : getPassengers()) {
instance.removeEntityFromChunk(passenger, lastChunk);
instance.addEntityToChunk(passenger, newChunk);
}
}
if (this instanceof Player)
((Player) this).onChunkChange(lastChunk, newChunk); // Refresh loaded chunk
// Refresh entity viewable list
long[] lastVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()), Main.ENTITY_VIEW_DISTANCE);
long[] updatedVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), Main.ENTITY_VIEW_DISTANCE);
boolean isPlayer = this instanceof Player;
int[] oldChunksEntity = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunksEntity, updatedVisibleChunksEntity);
for (int index : oldChunksEntity) {
int[] chunkPos = ChunkUtils.getChunkCoord(lastVisibleChunksEntity[index]);
Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]);
if (chunk == null)
continue;
instance.getChunkEntities(chunk).forEach(entity -> {
if (entity instanceof Player) {
Player player = (Player) entity;
removeViewer(player);
if (isPlayer) {
player.removeViewer((Player) this);
}
} else if (isPlayer) {
entity.removeViewer((Player) this);
}
});
}
int[] newChunksEntity = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunksEntity, lastVisibleChunksEntity);
for (int index : newChunksEntity) {
int[] chunkPos = ChunkUtils.getChunkCoord(updatedVisibleChunksEntity[index]);
Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]);
if (chunk == null)
continue;
instance.getChunkEntities(chunk).forEach(entity -> {
if (entity instanceof Player) {
Player player = (Player) entity;
addViewer(player);
if (this instanceof Player) {
player.addViewer((Player) this);
}
} else if (isPlayer) {
entity.addViewer((Player) this);
}
});
updateView(this, lastChunk, newChunk);
for (Entity passenger : getPassengers()) {
updateView(passenger, lastChunk, newChunk);
}
}
@ -491,6 +474,55 @@ public abstract class Entity implements Viewable, DataContainer {
refreshPosition(position.getX(), position.getY(), position.getZ());
}
private void updateView(Entity entity, Chunk lastChunk, Chunk newChunk) {
if (entity instanceof Player)
((Player) entity).onChunkChange(lastChunk, newChunk); // Refresh loaded chunk
// Refresh entity viewable list
long[] lastVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()), Main.ENTITY_VIEW_DISTANCE);
long[] updatedVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), Main.ENTITY_VIEW_DISTANCE);
boolean isPlayer = entity instanceof Player;
int[] oldChunksEntity = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunksEntity, updatedVisibleChunksEntity);
for (int index : oldChunksEntity) {
int[] chunkPos = ChunkUtils.getChunkCoord(lastVisibleChunksEntity[index]);
Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]);
if (chunk == null)
continue;
instance.getChunkEntities(chunk).forEach(ent -> {
if (ent instanceof Player) {
Player player = (Player) ent;
removeViewer(player);
if (isPlayer) {
player.removeViewer((Player) entity);
}
} else if (isPlayer) {
ent.removeViewer((Player) entity);
}
});
}
int[] newChunksEntity = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunksEntity, lastVisibleChunksEntity);
for (int index : newChunksEntity) {
int[] chunkPos = ChunkUtils.getChunkCoord(updatedVisibleChunksEntity[index]);
Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]);
if (chunk == null)
continue;
instance.getChunkEntities(chunk).forEach(ent -> {
if (ent instanceof Player) {
Player player = (Player) ent;
addViewer(player);
if (entity instanceof Player) {
player.addViewer((Player) entity);
}
} else if (isPlayer) {
ent.addViewer((Player) entity);
}
});
}
}
public void refreshView(float yaw, float pitch) {
this.lastYaw = position.getYaw();
this.lastPitch = position.getPitch();
@ -529,6 +561,10 @@ public abstract class Entity implements Viewable, DataContainer {
this.scheduledRemoveTime = System.currentTimeMillis() + delay;
}
public boolean isRemoveScheduled() {
return scheduledRemoveTime != 0;
}
protected EntityVelocityPacket getVelocityPacket() {
final float strength = 8000f / Main.TICK_PER_SECOND;
EntityVelocityPacket velocityPacket = new EntityVelocityPacket();
@ -605,12 +641,13 @@ public abstract class Entity implements Viewable, DataContainer {
buffer.putByte(index0);
}
protected void sendPositionSynchronization() {
protected void sendSynchronization() {
EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket();
entityTeleportPacket.entityId = getEntityId();
entityTeleportPacket.position = getPosition();
entityTeleportPacket.onGround = isOnGround();
sendPacketToViewers(entityTeleportPacket);
sendPacketToViewers(getPassengersPacket());
}
private void fillAirTickMetaData(Buffer buffer) {

View File

@ -1,10 +1,7 @@
package fr.themode.minestom.entity;
import fr.themode.minestom.event.DeathEvent;
import fr.themode.minestom.net.packet.server.play.EntityHeadLookPacket;
import fr.themode.minestom.net.packet.server.play.EntityLookAndRelativeMovePacket;
import fr.themode.minestom.net.packet.server.play.EntityPacket;
import fr.themode.minestom.net.packet.server.play.SpawnMobPacket;
import fr.themode.minestom.net.packet.server.play.*;
import fr.themode.minestom.net.player.PlayerConnection;
import fr.themode.minestom.utils.ChunkUtils;
import fr.themode.minestom.utils.EntityUtils;
@ -22,8 +19,7 @@ public abstract class EntityCreature extends LivingEntity {
super.update();
}
public void move(float x, float y, float z) {
// TODO change yaw/pitch based on next position?
public void move(float x, float y, float z, boolean updateView) {
Position position = getPosition();
float newX = position.getX() + x;
float newY = position.getY() + y;
@ -38,33 +34,42 @@ public abstract class EntityCreature extends LivingEntity {
float yaw = (float) (radians * (180.0 / Math.PI)) - 90;
float pitch = position.getPitch(); // TODO
EntityLookAndRelativeMovePacket entityLookAndRelativeMovePacket = new EntityLookAndRelativeMovePacket();
entityLookAndRelativeMovePacket.entityId = getEntityId();
entityLookAndRelativeMovePacket.deltaX = (short) ((newX * 32 - position.getX() * 32) * 128);
entityLookAndRelativeMovePacket.deltaY = (short) ((newY * 32 - position.getY() * 32) * 128);
entityLookAndRelativeMovePacket.deltaZ = (short) ((newZ * 32 - position.getZ() * 32) * 128);
entityLookAndRelativeMovePacket.yaw = yaw;
entityLookAndRelativeMovePacket.pitch = pitch;
entityLookAndRelativeMovePacket.onGround = isOnGround();
sendPacketToViewers(entityLookAndRelativeMovePacket);
if (updateView) {
EntityLookAndRelativeMovePacket entityLookAndRelativeMovePacket = new EntityLookAndRelativeMovePacket();
entityLookAndRelativeMovePacket.entityId = getEntityId();
entityLookAndRelativeMovePacket.deltaX = (short) ((newX * 32 - position.getX() * 32) * 128);
entityLookAndRelativeMovePacket.deltaY = (short) ((newY * 32 - position.getY() * 32) * 128);
entityLookAndRelativeMovePacket.deltaZ = (short) ((newZ * 32 - position.getZ() * 32) * 128);
entityLookAndRelativeMovePacket.yaw = yaw;
entityLookAndRelativeMovePacket.pitch = pitch;
entityLookAndRelativeMovePacket.onGround = isOnGround();
sendPacketToViewers(entityLookAndRelativeMovePacket);
} else {
EntityRelativeMovePacket entityRelativeMovePacket = new EntityRelativeMovePacket();
entityRelativeMovePacket.entityId = getEntityId();
entityRelativeMovePacket.deltaX = (short) ((newX * 32 - position.getX() * 32) * 128);
entityRelativeMovePacket.deltaY = (short) ((newY * 32 - position.getY() * 32) * 128);
entityRelativeMovePacket.deltaZ = (short) ((newZ * 32 - position.getZ() * 32) * 128);
entityRelativeMovePacket.onGround = isOnGround();
sendPacketToViewers(entityRelativeMovePacket);
}
if (lastYaw != yaw) {
EntityHeadLookPacket entityHeadLookPacket = new EntityHeadLookPacket();
entityHeadLookPacket.entityId = getEntityId();
entityHeadLookPacket.yaw = yaw;
sendPacketToViewers(entityHeadLookPacket);
refreshView(yaw, pitch);
}
refreshPosition(newX, newY, newZ);
refreshView(yaw, pitch);
}
public void moveTowards(Position direction, float speed) {
float radians = (float) Math.atan2(direction.getZ() - position.getZ(), direction.getX() - position.getX());
float speedX = (float) (Math.cos(radians) * speed);
float speedZ = (float) (Math.sin(radians) * speed);
move(speedX, 0, speedZ);
move(speedX, 0, speedZ, true);
}
@Override

View File

@ -15,6 +15,7 @@ public class ItemEntity extends ObjectEntity {
public ItemEntity(ItemStack itemStack) {
super(34);
this.itemStack = itemStack;
setBoundingBox(0.25f, 0.25f, 0.25f);
setGravity(0.02f);
}

View File

@ -1,6 +1,7 @@
package fr.themode.minestom.entity;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.collision.BoundingBox;
import fr.themode.minestom.entity.property.Attribute;
import fr.themode.minestom.event.PickupItemEvent;
import fr.themode.minestom.instance.Chunk;
@ -37,15 +38,16 @@ public abstract class LivingEntity extends Entity {
if (canPickupItem) {
Chunk chunk = instance.getChunkAt(getPosition()); // TODO check surrounding chunks
Set<Entity> entities = instance.getChunkEntities(chunk);
BoundingBox livingBoundingBox = getBoundingBox().expand(1, 0.5f, 1);
for (Entity entity : entities) {
if (entity instanceof ItemEntity) {
ItemEntity itemEntity = (ItemEntity) entity;
if (!itemEntity.isPickable())
continue;
float distance = getDistance(entity);
if (distance <= 2.04) {
BoundingBox itemBoundingBox = itemEntity.getBoundingBox();
if (livingBoundingBox.intersect(itemBoundingBox)) {
synchronized (itemEntity) {
if (itemEntity.shouldRemove())
if (itemEntity.shouldRemove() || itemEntity.isRemoveScheduled())
continue;
ItemStack item = itemEntity.getItemStack();
PickupItemEvent pickupItemEvent = new PickupItemEvent(item);
@ -55,7 +57,7 @@ public abstract class LivingEntity extends Entity {
collectItemPacket.collectorEntityId = getEntityId();
collectItemPacket.pickupItemCount = item.getAmount();
sendPacketToViewersAndSelf(collectItemPacket);
entity.remove();
entity.scheduleRemove(500);
});
}
}

View File

@ -4,6 +4,7 @@ import fr.themode.minestom.Main;
import fr.themode.minestom.bossbar.BossBar;
import fr.themode.minestom.chat.Chat;
import fr.themode.minestom.data.Data;
import fr.themode.minestom.entity.demo.ChickenCreature;
import fr.themode.minestom.entity.property.Attribute;
import fr.themode.minestom.event.*;
import fr.themode.minestom.instance.Chunk;
@ -81,6 +82,8 @@ public class Player extends LivingEntity {
// Vehicle
private float sideways;
private float forward;
private boolean jump;
private boolean unmount;
protected boolean spawned;
@ -90,6 +93,8 @@ public class Player extends LivingEntity {
this.username = username;
this.playerConnection = playerConnection;
setBoundingBox(0.8f, 1.8f, 0.8f);
playerConnection.sendPacket(getPropertiesPacket()); // Send default properties
refreshHealth();
@ -151,11 +156,12 @@ public class Player extends LivingEntity {
setGameMode(GameMode.SURVIVAL);
teleport(new Position(0, 66, 0));
/*ChickenCreature chickenCreature = new ChickenCreature();
ChickenCreature chickenCreature = new ChickenCreature();
chickenCreature.refreshPosition(2, 65, 2);
chickenCreature.setInstance(getInstance());
chickenCreature.addPassenger(this);
for (int ix = 0; ix < 4; ix++)
/*for (int ix = 0; ix < 4; ix++)
for (int iz = 0; iz < 4; iz++) {
ItemEntity itemEntity = new ItemEntity(new ItemStack(1, (byte) 32));
itemEntity.refreshPosition(ix, 68, iz);
@ -582,6 +588,14 @@ public class Player extends LivingEntity {
return forward;
}
public boolean isVehicleJump() {
return jump;
}
public boolean isVehicleUnmount() {
return unmount;
}
public void openInventory(Inventory inventory) {
if (inventory == null)
throw new IllegalArgumentException("Inventory cannot be null, use Player#closeInventory() to close current");
@ -695,9 +709,11 @@ public class Player extends LivingEntity {
this.bossBars.remove(bossBar);
}
public void refreshVehicleSteer(float sideways, float forward) {
public void refreshVehicleSteer(float sideways, float forward, boolean jump, boolean unmount) {
this.sideways = sideways;
this.forward = forward;
this.jump = jump;
this.unmount = unmount;
}
public long getLastKeepAlive() {

View File

@ -1,11 +1,15 @@
package fr.themode.minestom.entity.demo;
import fr.themode.minestom.Main;
import fr.themode.minestom.entity.Entity;
import fr.themode.minestom.entity.EntityCreature;
import fr.themode.minestom.entity.EntityType;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.utils.Position;
import net.tofweb.starlite.*;
import fr.themode.minestom.utils.Vector;
import net.tofweb.starlite.CellSpace;
import net.tofweb.starlite.CostBlockManager;
import net.tofweb.starlite.Path;
import net.tofweb.starlite.Pathfinder;
public class ChickenCreature extends EntityCreature {
@ -17,44 +21,69 @@ public class ChickenCreature extends EntityCreature {
public ChickenCreature() {
super(EntityType.CHICKEN);
setBoundingBox(0.4f, 0.7f, 0.4f);
}
@Override
public void update() {
super.update();
float speed = 0.125f;
float speed = 0.25f;
/*if (hasPassenger()) {
if (hasPassenger()) {
Entity passenger = getPassengers().iterator().next();
if (passenger instanceof Player) {
Player player = (Player) passenger;
float sideways = player.getVehicleSideways();
float forward = player.getVehicleForward();
boolean jump = player.isVehicleJump();
boolean unmount = player.isVehicleUnmount();
if (jump && isOnGround()) {
setVelocity(new Vector(0, 6, 0), 500);
}
boolean updateView = forward > 0;
if (sideways == 0f && forward == 0f)
return;
float yaw = player.getYaw();
float yaw = player.getPosition().getYaw();
yaw %= 360;
sideways = yaw + (updateView ? -sideways * 90 : sideways * 90);
if (forward > 0) {
forward = yaw * forward;
} else {
forward = yaw - forward * 180;
forward = yaw + forward * 360;
}
sideways = yaw - sideways * 90;
yaw = (forward + sideways) / 2 % 360;
System.out.println("test: " + forward + " : " + sideways);
double radian = Math.toRadians(yaw + 90);
double cos = Math.cos(radian);
double sin = Math.sin(radian);
//System.out.println(sideways + " : " + forward);
//System.out.println(cos + " : " + sin);
double x = cos;
double z = sin;
move(x * speed, 0, z * speed);
}
}*/
float x = (float) cos * speed;
float z = (float) sin * speed;
if (path == null) {
/*BlockPosition blockPosition = getPosition().toBlockPosition();
BlockPosition belowPosition = blockPosition.clone().add(0, -1, 0);
BlockPosition upPosition = blockPosition.clone().add(0, 1, 0);
boolean airCurrent = getInstance().getBlockId(blockPosition) == 0;
boolean airBelow = getInstance().getBlockId(belowPosition) == 0;
boolean airUp = getInstance().getBlockId(upPosition) == 0;
boolean shouldJump = false;
int boundingBoxY = (int) Math.ceil(getBoundingBox().getY());
for (int i = 0; i < boundingBoxY; i++) {
}*/
//System.out.println("test: "+player.isVehicleJump());
//System.out.println(getInstance().getBlockId(getPosition().toBlockPosition()));
move(x, 0, z, updateView);
}
}
/*if (path == null) {
System.out.println("FIND PATH");
Player player = Main.getConnectionManager().getPlayer("Raulnil");
if (player != null) {
@ -88,7 +117,7 @@ public class ChickenCreature extends EntityCreature {
moveTowards(target, speed);
}
}
}
}*/
}
@Override

View File

@ -97,7 +97,7 @@ public class PlayerDiggingListener {
player.getInventory().setItemInMainHand(handItem);
ItemEntity itemEntity = new ItemEntity(droppedItem);
itemEntity.setPickupDelay(1000);
itemEntity.setPickupDelay(500);
itemEntity.refreshPosition(player.getPosition().clone().add(0, 1.5f, 0));
itemEntity.setInstance(player.getInstance());
Vector velocity = player.getPosition().clone().getDirection().multiply(5);

View File

@ -6,7 +6,10 @@ import fr.themode.minestom.net.packet.client.play.ClientSteerVehiclePacket;
public class PlayerVehicleListener {
public static void steerVehicleListener(ClientSteerVehiclePacket packet, Player player) {
player.refreshVehicleSteer(packet.sideways, packet.forward);
byte flags = packet.flags;
boolean jump = (flags & 0x1) != 0;
boolean unmount = (flags & 0x2) != 0;
player.refreshVehicleSteer(packet.sideways, packet.forward, jump, unmount);
}
}

View File

@ -48,6 +48,9 @@ public class BlockPosition {
this.z = z;
}
public BlockPosition clone() {
return new BlockPosition(x, y, z);
}
@Override
public String toString() {