Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Draycia 2020-05-03 23:55:44 -07:00
commit 5b97dde21f
21 changed files with 443 additions and 119 deletions

28
.github/CONTRIBUTING.md vendored Normal file
View File

@ -0,0 +1,28 @@
## How to contribute to Minestom
#### **Did you find a bug?**
* Open a new GitHub issue if it's not already reported.
* Use the relevant bug report template to create the issue.
#### **Did you write some code that fixes a bug?**
* Open a new GitHub pull-request with the commits if it hasn't already been proposed.
* Ensure the PR description clearly describes the problem and solution. Include the relevant issue number if applicable.
#### **Do you intend to add a new feature or change an existing one?**
* Do not open a pull-request on GitHub until you have collected positive feedback about the change from a maintainer.
#### **Do you have questions about the source code?**
* Ask any question about how to use Minestom in the GitHub issues section or the community portals.
#### **Do you want to contribute to the Minestom documentation?**
* Feel free to do so! Just make sure to conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification when editing the README.md.
## General Contribution Rules
* By contributing to the Minestom project your code/contribution will be licensed under the [Apache Version 2.0](../LICENSE) license.
Minestom is a community project. We encourage you to contribute! :)
Thanks! :heart: :heart: :heart:
~Minestom Community

22
.github/ISSUE_TEMPLATE/BUG_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,22 @@
---
name: Bug report
labels: Bug
about: Use this to report unexpected behavior (bugs and errors).
---
### What is the current behavior?
### What is the expected behavior?
### What steps will reproduce the problem?
### What operating system and java version is being used?
### If there is an exception, use pastebin/hastebin (NB: no expiry date!) to send the stacktrace
### Additional information / Possible thoughts on why this bug is happening

View File

@ -0,0 +1,15 @@
---
name: Feature Request
labels: Enhancement
about: Use this to request an addition (feature).
---
### What would you like added/changed?
### Why do you think this is a good addition/alteration?
### Why do you want this to be added?
### Additional Information

View File

@ -0,0 +1,7 @@
---
name: Question
labels: Question
about: Use this to ask a question.
---
### Your Question:

View File

@ -1,9 +1,33 @@
# About Minestom
Minestom is an alternative to the popular minecraft server API named Bukkit/Spigot. The main difference is that our implementation of Notchian server does not contain any features by default! However, we have a complete API which allows you to make anything possible with current spigot's plugins.
# Minestom
![banner](banner.png)
All planned features are listed on [trello](https://trello.com/b/4ysvj5hT/minestom)
[![license](https://img.shields.io/github/license/Minestom/Minestom.svg)](../LICENSE)
[![standard-readme compliant](https://img.shields.io/badge/readme%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/RichardLitt/standard-readme)
[![discord-banner](https://discordapp.com/api/guilds/706185253441634317/widget.png?style=banner2)](https://discord.gg/pkFRvqB)
An example of how to use the Minestom library is available [here](src/main/java/fr/themode/demo)
Minestom is an alternative to the popular Minecraft server API called Bukkit.
The main difference is that our implementation of the Notchian server does not contain any features by default!
However, we have a complete API which allows you to make anything possible with current spigot plugins.
# Table of contents
- [Install](#install)
- [Usage](#usage)
- [Why Minestom?](#why-minestom)
- [Advantages & Disadvantages](#advantages-and-disadvantages)
- [API](#api)
- [Contributing](#contributing)
- [License](#license)
# Install
Minestom is similar to Bukkit in the fact that it is not a standlone program, it must be expanded upon.
It is the base for interfacing between the server and client.
Our own expanded version for Vanilla can be found [here](https://github.com/Minestom/VanillaReimplementation).
# Usage
An example of how to use the Minestom library is available [here](https://github.com/Minestom/Minestom/tree/master/src/main/java/fr/themode/demo).
Alternatively you can check the official wiki [here](https://github.com/Minestom/Minestom/wiki).
# Why Minestom?
Minecraft evolved a lot since its release, most of the servers today do not take advantage of vanilla features and even have to struggle because of them. Our target audience is those who want to make a completely different server compared to default Minecraft gamemode such as survival or creative building.
@ -11,7 +35,26 @@ The goal is to offer more performance for those who need it, Minecraft being sin
In other words, it makes sense to use Minestom when it takes less time to implement everything you want than removing everything you don't need.
# API features
# Advantages and Disadvantages
Minestom isn't perfect, our choices make it much better for some cases, worse for some others.
## Advantages
* Remove the overhead of vanilla features
* Multi-threaded (Chunks & Entities)
* 100% Thread-Safe
* Instance system which is much more scalable than worlds
* Open-source
* Modern API
* No more disgusting NMS
## Disadvantages
* Does not work with Bukkit/Spigot plugins
* Does not work with older clients
* Bad for those who want a vanilla experience
* Longer to develop something playable
* Multi-threaded environments are prone to complications
# API
Even if we do not include anything by default in the game, we simplify the way you add them, here is a preview.
## Instances
@ -36,21 +79,11 @@ It is a field where Minecraft evolved a lot, inventories are now used a lot as c
## Commands
Commands are the simplest way of communication between clients and server. Since 1.13 Minecraft has incorporated a new library denominated "Brigadier", we then integrated an API meant to use the full potential of args types.
# Pros & Cons
Minestom isn't perfect, our choices make it much better for some cases, worse for some others.
# Contributing
See [the contributing file](CONTRIBUTING.md)!
All planned features are listed on [Trello](https://trello.com/b/4ysvj5hT/minestom)
# License
This project is licensed under the [Apache License Version 2.0](../LICENSE).
## Pros
* Remove the overhead of vanilla features
* Multi-threaded (Chunks & Entities)
* 100% Thread-Safe
* Instance system which is much more scalable than worlds
* Open-source
* Modern API
* No more disgusting NMS
## Cons
* Does not work with bukkit plugins
* Does not work with older clients
* Bad for those who want vanilla experience
* Longer to obtain something playable
* Multi-threaded environments are prone to complications

BIN
.github/banner.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@ -1,5 +1,5 @@
plugins {
id 'java'
id 'java-library'
id 'net.ltgt.apt' version '0.10'
id 'com.github.johnrengelman.shadow' version '4.0.4'
}
@ -19,27 +19,27 @@ dependencies {
testCompile group: 'junit', name: 'junit', version: '4.12'
// https://mvnrepository.com/artifact/io.netty/netty-all
compile group: 'io.netty', name: 'netty-all', version: '4.1.48.Final'
api group: 'io.netty', name: 'netty-all', version: '4.1.48.Final'
implementation 'com.github.jhg023:Pbbl:1.0.1'
api 'com.github.jhg023:Pbbl:1.0.1'
// https://mvnrepository.com/artifact/it.unimi.dsi/fastutil
implementation group: 'it.unimi.dsi', name: 'fastutil', version: '8.3.0'
api group: 'it.unimi.dsi', name: 'fastutil', version: '8.3.0'
implementation 'com.github.Querz:NBT:4.1'
implementation 'com.github.luben:zstd-jni:1.4.3-1'
implementation 'com.esotericsoftware:reflectasm:1.11.9'
api 'com.github.Querz:NBT:4.1'
api 'com.github.luben:zstd-jni:1.4.3-1'
api 'com.esotericsoftware:reflectasm:1.11.9'
// https://mvnrepository.com/artifact/com.google.code.gson/gson
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
api group: 'com.google.code.gson', name: 'gson', version: '2.8.5'
compile 'com.github.TheMode:CommandBuilder:f893cfbfe4'
api 'com.github.TheMode:CommandBuilder:f893cfbfe4'
// https://jitpack.io/#Articdive/Jnoise/1.0-SNAPSHOT
compile 'com.github.Articdive:Jnoise:1.0-SNAPSHOT'
api 'com.github.Articdive:Jnoise:1.0-SNAPSHOT'
// https://mvnrepository.com/artifact/javax.vecmath/vecmath
compile group: 'javax.vecmath', name: 'vecmath', version: '1.5.2' // Used for Fastnoise
api group: 'javax.vecmath', name: 'vecmath', version: '1.5.2' // Used for Fastnoise
compile 'net.kyori:text-api:3.0.3'
compile 'net.kyori:text-serializer-legacy:3.0.3'

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip

View File

@ -196,6 +196,15 @@ public class PlayerInit {
BelowNameScoreboard belowNameScoreboard = new BelowNameScoreboard();
setBelowNameScoreboard(belowNameScoreboard);
belowNameScoreboard.updateScore(this, 50);*/
player.addEventCallback(PlayerUseItemEvent.class, useEvent -> {
player.sendMessage("Using item in air: "+useEvent.getItemStack().getMaterial());
});
player.addEventCallback(PlayerUseItemOnBlockEvent.class, useEvent -> {
player.sendMessage("Main item: "+player.getInventory().getItemInMainHand().getMaterial());
player.sendMessage("Using item on block: "+useEvent.getItemStack().getMaterial()+" at "+useEvent.getPosition()+" on face "+useEvent.getBlockFace());
});
});
});
}

View File

@ -3,6 +3,7 @@ package net.minestom.server.collision;
import net.minestom.server.entity.Entity;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.Vector;
/**
* See https://wiki.vg/Entity_metadata#Mobs_2
@ -105,6 +106,60 @@ public class BoundingBox {
return entity.getPosition().getZ() + (z / 2);
}
public Vector[] getBottomFace() {
return new Vector[] {
new Vector(getMinX(), getMinY(), getMinZ()),
new Vector(getMaxX(), getMinY(), getMinZ()),
new Vector(getMaxX(), getMinY(), getMaxZ()),
new Vector(getMinX(), getMinY(), getMaxZ()),
};
}
public Vector[] getTopFace() {
return new Vector[] {
new Vector(getMinX(), getMaxY(), getMinZ()),
new Vector(getMaxX(), getMaxY(), getMinZ()),
new Vector(getMaxX(), getMaxY(), getMaxZ()),
new Vector(getMinX(), getMaxY(), getMaxZ()),
};
}
public Vector[] getLeftFace() {
return new Vector[] {
new Vector(getMinX(), getMinY(), getMinZ()),
new Vector(getMinX(), getMaxY(), getMinZ()),
new Vector(getMinX(), getMaxY(), getMaxZ()),
new Vector(getMinX(), getMinY(), getMaxZ()),
};
}
public Vector[] getRightFace() {
return new Vector[] {
new Vector(getMaxX(), getMinY(), getMinZ()),
new Vector(getMaxX(), getMaxY(), getMinZ()),
new Vector(getMaxX(), getMaxY(), getMaxZ()),
new Vector(getMaxX(), getMinY(), getMaxZ()),
};
}
public Vector[] getFrontFace() {
return new Vector[] {
new Vector(getMinX(), getMinY(), getMinZ()),
new Vector(getMaxX(), getMinY(), getMinZ()),
new Vector(getMaxX(), getMaxY(), getMinZ()),
new Vector(getMinX(), getMaxY(), getMinZ()),
};
}
public Vector[] getBackFace() {
return new Vector[] {
new Vector(getMinX(), getMinY(), getMaxZ()),
new Vector(getMaxX(), getMinY(), getMaxZ()),
new Vector(getMaxX(), getMaxY(), getMaxZ()),
new Vector(getMinX(), getMaxY(), getMaxZ()),
};
}
@Override
public String toString() {
String result = "BoudingBox";

View File

@ -9,6 +9,10 @@ import net.minestom.server.utils.Vector;
public class CollisionUtils {
private static final Vector Y_AXIS = new Vector(0, 1, 0);
private static final Vector X_AXIS = new Vector(1, 0, 0);
private static final Vector Z_AXIS = new Vector(0, 0, 1);
/**
* Moves an entity with physics applied (ie checking against blocks)
* @param entity the entity to move
@ -21,78 +25,137 @@ public class CollisionUtils {
Position currentPosition = entity.getPosition();
BoundingBox boundingBox = entity.getBoundingBox();
float currentX = currentPosition.getX();
float currentY = currentPosition.getY();
float currentZ = currentPosition.getZ();
Vector intermediaryPosition = new Vector();
boolean yCollision = stepAxis(instance, currentPosition.toVector(), Y_AXIS, deltaPosition.getY(),
intermediaryPosition,
deltaPosition.getY() > 0 ? boundingBox.getTopFace() : boundingBox.getBottomFace()
);
// target_WithBB is the target_ with the length in the _ direction of the bounding box added. Used to determinate block intersections
boolean xCollision = stepAxis(instance, intermediaryPosition, X_AXIS, deltaPosition.getX(),
intermediaryPosition,
deltaPosition.getX() < 0 ? boundingBox.getLeftFace() : boundingBox.getRightFace()
);
// step Y
float targetY = entity.getPosition().getY() + deltaPosition.getY();
float targetYWithBB = targetY;
if(deltaPosition.getY() > 0) {
targetYWithBB += boundingBox.getHeight();
}
BlockPosition yBlock = new BlockPosition(currentX, (int) targetYWithBB, currentZ);
boolean yAir = !Block.fromId(instance.getBlockId(yBlock)).isSolid();
boolean yIntersect = boundingBox.intersect(yBlock);
boolean zCollision = stepAxis(instance, intermediaryPosition, Z_AXIS, deltaPosition.getZ(),
intermediaryPosition,
deltaPosition.getZ() > 0 ? boundingBox.getBackFace() : boundingBox.getFrontFace()
);
boolean yCollision = true;
if(yAir || !yIntersect)
yCollision = false;
float newY = yCollision ? currentY : targetY;
if(yCollision) {
if(deltaPosition.getY() < 0) {
newY = (float) (Math.ceil(newY)+0.01f); // TODO: custom block bounding boxes
} else if(deltaPosition.getY() > 0) {
newY = (float) (Math.floor(newY)-0.01f); // TODO: custom block bounding boxes
}
}
// step X/Z
float targetX = entity.getPosition().getX() + deltaPosition.getX();
float targetXWithBB = targetX+Math.signum(deltaPosition.getX()) * boundingBox.getWidth()/2f;
float targetZ = entity.getPosition().getZ() + deltaPosition.getZ();
float targetZWithBB = targetZ+Math.signum(deltaPosition.getZ()) * boundingBox.getDepth()/2f;
BlockPosition xBlock = new BlockPosition(targetXWithBB, (int) newY, currentZ);
BlockPosition zBlock = new BlockPosition(currentX, (int) newY, targetZWithBB);
boolean xAir = !Block.fromId(instance.getBlockId(xBlock)).isSolid();
boolean zAir = !Block.fromId(instance.getBlockId(zBlock)).isSolid();
boolean xIntersect = boundingBox.intersect(xBlock);
boolean zIntersect = boundingBox.intersect(zBlock);
boolean xCollision = true;
if(xAir || !xIntersect)
xCollision = false;
boolean zCollision = true;
if(zAir || !zIntersect)
zCollision = false;
float newX = xCollision ? currentX : targetX;
float newZ = zCollision ? currentZ : targetZ;
velocityOut.copy(entity.getVelocity());
if(xCollision) {
positionOut.setX(intermediaryPosition.getX());
positionOut.setY(intermediaryPosition.getY());
positionOut.setZ(intermediaryPosition.getZ());
velocityOut.copy(deltaPosition);
if (xCollision) {
velocityOut.setX(0f);
}
if(yCollision) {
if (yCollision) {
velocityOut.setY(0f);
}
if(zCollision) {
if (zCollision) {
velocityOut.setZ(0f);
}
positionOut.setX(newX);
positionOut.setY(newY);
positionOut.setZ(newZ);
return yCollision && deltaPosition.getY() < 0;
}
/**
* Steps on a single axis. Checks against collisions for each point of 'corners'. This method assumes that startPosition is valid.
* Immediately return false if corners is of length 0.
* @param instance instance to check blocks from
* @param startPosition starting position for stepping, can be intermediary position from last step
* @param axis step direction. Works best if unit vector and aligned to an axis
* @param stepAmount how much to step in the direction (in blocks)
* @param positionOut the vector in which to store the new position
* @param corners the corners to check against
* @return true iif a collision has been found
*/
private static boolean stepAxis(Instance instance, Vector startPosition, Vector axis, float stepAmount, Vector positionOut, Vector... corners) {
positionOut.copy(startPosition);
if (corners.length == 0)
return false; // avoid degeneracy in following computations
// perform copies to allow in place modifications
// prevents making a lot of new objects. Well at least it reduces the count
BlockPosition[] cornerPositions = new BlockPosition[corners.length];
Vector[] cornersCopy = new Vector[corners.length];
for (int i = 0; i < corners.length; i++) {
cornersCopy[i] = corners[i].clone();
cornerPositions[i] = new BlockPosition(corners[i]);
}
float sign = Math.signum(stepAmount);
int blockLength = (int)stepAmount;
float remainingLength = stepAmount-blockLength;
// used to determine if 'remainingLength' should be used
boolean collisionFound = false;
for (int i = 0; i < Math.abs(blockLength); i++) {
if (!stepOnce(instance, axis, sign, cornersCopy, cornerPositions)) {
collisionFound = true;
}
if(collisionFound) {
break;
}
}
// add remainingLength
if(!collisionFound) {
Vector direction = new Vector();
direction.copy(axis);
collisionFound |= !stepOnce(instance, direction, remainingLength, cornersCopy, cornerPositions);
}
// find the corner which moved the least
float smallestDisplacement = Float.POSITIVE_INFINITY;
for (int i = 0; i < corners.length; i++) {
float displacement = (float) corners[i].distance(cornersCopy[i]);
if (displacement < smallestDisplacement) {
smallestDisplacement = displacement;
}
}
positionOut.copy(startPosition);
positionOut.add(smallestDisplacement*axis.getX()*sign, smallestDisplacement*axis.getY()*sign, smallestDisplacement*axis.getZ()*sign);
return collisionFound;
}
/**
* Steps once (by a length of 1 block) on the given axis. Returns false if this method encountered a collision
* @param instance instance to get blocks from
* @param axis the axis to move along
* @param cornersCopy the corners of the bounding box to consider (mutable)
* @param cornerPositions the corners, converted to BlockPosition (mutable)
* @return
*/
private static boolean stepOnce(Instance instance, Vector axis, float amount, Vector[] cornersCopy, BlockPosition[] cornerPositions) {
float sign = Math.signum(amount);
for (int cornerIndex = 0; cornerIndex < cornersCopy.length; cornerIndex++) {
Vector corner = cornersCopy[cornerIndex];
BlockPosition blockPos = cornerPositions[cornerIndex];
corner.add(axis.getX()*amount, axis.getY()*amount, axis.getZ()*amount);
blockPos.setX((int) Math.floor(corner.getX()));
blockPos.setY((int) Math.floor(corner.getY()));
blockPos.setZ((int) Math.floor(corner.getZ()));
short blockId = instance.getBlockId(blockPos);
Block block = Block.fromId(blockId);
// TODO: block collision boxes
// TODO: for the moment, always consider a full block
if (block.isSolid()) {
corner.subtract(axis.getX()*amount, axis.getY()*amount, axis.getZ()*amount);
if (Math.abs(axis.getX()) > 10e-16) {
corner.setX(blockPos.getX() - axis.getX() * sign);
}
if (Math.abs(axis.getY()) > 10e-16) {
corner.setY(blockPos.getY() - axis.getY() * sign);
}
if (Math.abs(axis.getZ()) > 10e-16) {
corner.setZ(blockPos.getZ() - axis.getZ() * sign);
}
return false;
}
}
return true;
}
}

View File

@ -249,17 +249,17 @@ public abstract class Entity implements Viewable, DataContainer {
velocity.setY(velocity.getY() - gravityDragPerTick*tps);
}
Position newPositionOut = new Position();
Vector newVelocityOut = new Vector();
Vector deltaPos = new Vector(
getVelocity().getX() / tps,
getVelocity().getY() / tps,
getVelocity().getZ() / tps
getVelocity().getX()/tps,
getVelocity().getY()/tps,
getVelocity().getZ()/tps
);
onGround = CollisionUtils.handlePhysics(this, deltaPos, newPosition, newVelocityOut);
refreshPosition(newPosition);
velocity.copy(newVelocityOut);
velocity.multiply(tps);
float drag;
if(onGround) {
@ -267,7 +267,8 @@ public abstract class Entity implements Viewable, DataContainer {
} else {
drag = 0.98f; // air drag
}
velocity.multiply(drag);
velocity.setX(velocity.getX() * drag);
velocity.setZ(velocity.getZ() * drag);
sendSynchronization();

View File

@ -3,6 +3,9 @@ package net.minestom.server.event;
import net.minestom.server.entity.Player;
import net.minestom.server.item.ItemStack;
/**
* Event when an item is used without clicking a block
*/
public class PlayerUseItemEvent extends CancellableEvent {
private Player.Hand hand;

View File

@ -0,0 +1,40 @@
package net.minestom.server.event;
import net.minestom.server.entity.Player;
import net.minestom.server.item.ItemStack;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.Direction;
/**
* Used when a player is clicking a block with an item (but is not a block in item form)
*/
public class PlayerUseItemOnBlockEvent extends Event {
private Player.Hand hand;
private ItemStack itemStack;
private final BlockPosition position;
private final Direction blockFace;
public PlayerUseItemOnBlockEvent(Player.Hand hand, ItemStack itemStack, BlockPosition position, Direction blockFace) {
this.hand = hand;
this.itemStack = itemStack;
this.position = position;
this.blockFace = blockFace;
}
public BlockPosition getPosition() {
return position;
}
public Direction getBlockFace() {
return blockFace;
}
public Player.Hand getHand() {
return hand;
}
public ItemStack getItemStack() {
return itemStack;
}
}

View File

@ -291,10 +291,8 @@ public class Chunk implements Viewable {
short blockId = getBlockId(x, y, z);
short customBlockId = getCustomBlockId(x, y, z);
boolean isCustomBlock = customBlockId != 0;
short id = isCustomBlock ? customBlockId : blockId;
if (id == 0)
if (blockId == 0 && customBlockId == 0)
continue;
Data data = blocksData.get(index);
@ -305,8 +303,8 @@ public class Chunk implements Viewable {
dos.writeInt(y);
dos.writeInt(z);
dos.writeBoolean(isCustomBlock); // Determine the type of the ID
dos.writeShort(id);
dos.writeShort(blockId);
dos.writeShort(customBlockId);
hasData = (data != null && (data instanceof SerializableData)) && hasData;
dos.writeBoolean(hasData);

View File

@ -33,8 +33,9 @@ public class ChunkReader {
int y = stream.readInt();
int z = stream.readInt();
boolean isCustomBlock = stream.readBoolean();
short blockId = stream.readShort();
short customBlockId = stream.readShort();
boolean hasData = stream.readBoolean();
Data data = null;
@ -45,8 +46,8 @@ public class ChunkReader {
data = DataReader.readData(Unpooled.wrappedBuffer(dataArray));
}
if (isCustomBlock) {
chunkBatch.setCustomBlock(x, y, z, blockId, data);
if (customBlockId != 0) {
chunkBatch.setSeparateBlocks(x, y, z, blockId, customBlockId, data);
} else {
chunkBatch.setBlock(x, y, z, blockId, data);
}

View File

@ -7,6 +7,7 @@ import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player;
import net.minestom.server.event.PlayerBlockInteractEvent;
import net.minestom.server.event.PlayerBlockPlaceEvent;
import net.minestom.server.event.PlayerUseItemOnBlockEvent;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.block.Block;
@ -56,7 +57,7 @@ public class BlockPlacementListener {
// Check if item at hand is a block
ItemStack usedItem = hand == Player.Hand.MAIN ? playerInventory.getItemInMainHand() : playerInventory.getItemInOffHand();
Material material = Material.fromId(usedItem.getMaterialId());
if (material != null && !material.isBlock()) {
if(material == Material.AIR) {
return;
}
@ -122,6 +123,8 @@ public class BlockPlacementListener {
refreshChunk = true;
}
} else {
PlayerUseItemOnBlockEvent event = new PlayerUseItemOnBlockEvent(hand, usedItem, blockPosition, blockFace.toDirection());
player.callEvent(PlayerUseItemOnBlockEvent.class, event);
refreshChunk = true;
}

View File

@ -3,6 +3,7 @@ package net.minestom.server.network.packet.client.play;
import net.minestom.server.network.packet.PacketReader;
import net.minestom.server.network.packet.client.ClientPlayPacket;
import net.minestom.server.utils.BlockPosition;
import net.minestom.server.utils.Direction;
public class ClientPlayerDiggingPacket extends ClientPlayPacket {
@ -28,12 +29,22 @@ public class ClientPlayerDiggingPacket extends ClientPlayPacket {
}
public enum BlockFace {
BOTTOM,
TOP,
NORTH,
SOUTH,
WEST,
EAST
BOTTOM(Direction.DOWN),
TOP(Direction.UP),
NORTH(Direction.NORTH),
SOUTH(Direction.SOUTH),
WEST(Direction.WEST),
EAST(Direction.EAST);
private final Direction direction;
BlockFace(Direction direction) {
this.direction = direction;
}
public Direction toDirection() {
return direction;
}
}
}

View File

@ -1,5 +1,7 @@
package net.minestom.server.utils;
import java.util.Objects;
// TODO: pool block positions?
public class BlockPosition {
@ -19,6 +21,10 @@ public class BlockPosition {
this.z = (int) Math.floor(z);
}
public BlockPosition(Vector position) {
this(position.getX(), position.getY(), position.getZ());
}
public BlockPosition add(int x, int y, int z) {
this.x += x;
this.y += y;
@ -71,6 +77,21 @@ public class BlockPosition {
return new Position(x, y, z);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BlockPosition that = (BlockPosition) o;
return x == that.x &&
y == that.y &&
z == that.z;
}
@Override
public int hashCode() {
return Objects.hash(x, y, z);
}
@Override
public String toString() {
return "BlockPosition[" + x + ":" + y + ":" + z + "]";

View File

@ -143,6 +143,10 @@ public class Position {
return new BlockPosition(x, y, z);
}
public Vector toVector() {
return new Vector(x, y, z);
}
@Override
public String toString() {
return "Position[" + x + ":" + y + ":" + z + "] (" + yaw + "/" + pitch + ")";

View File

@ -25,6 +25,13 @@ public class Vector implements Cloneable {
return this;
}
public Vector add(float x, float y, float z) {
this.x += x;
this.y += y;
this.z += z;
return this;
}
/**
* Subtracts a vector from this one.
*
@ -38,6 +45,13 @@ public class Vector implements Cloneable {
return this;
}
public Vector subtract(float x, float y, float z) {
this.x -= x;
this.y -= y;
this.z -= z;
return this;
}
/**
* Multiplies the vector by another.
*
@ -206,11 +220,7 @@ public class Vector implements Cloneable {
*/
@Override
public Vector clone() {
try {
return (Vector) super.clone();
} catch (CloneNotSupportedException e) {
throw new Error(e);
}
return new Vector(x, y, z);
}
public float getX() {