More comments + cleanup

This commit is contained in:
Felix Cravic 2020-07-22 17:39:48 +02:00
parent 7c6f919120
commit df6c9e4953
14 changed files with 162 additions and 74 deletions

View File

@ -57,9 +57,9 @@ public class BoundingBox {
float minZ = z;
float maxZ = z + offsetZ;
boolean checkX = getMinX() < maxX && getMaxX() > minX;
boolean checkY = getMinY() < maxY && getMaxY() > minY;
boolean checkZ = getMinZ() < maxZ && getMaxZ() > minZ;
final boolean checkX = getMinX() < maxX && getMaxX() > minX;
final boolean checkY = getMinY() < maxY && getMaxY() > minY;
final boolean checkZ = getMinZ() < maxZ && getMaxZ() > minZ;
return checkX && checkY && checkZ;
}
@ -75,6 +75,8 @@ public class BoundingBox {
}
/**
* Create a new bounding box linked to the same entity with expanded size
*
* @param x the X offset
* @param y the Y offset
* @param z the Z offset
@ -85,6 +87,8 @@ public class BoundingBox {
}
/**
* Create a new bounding box linked to the same entity with contracted size
*
* @param x the X offset
* @param y the Y offset
* @param z the Z offset

View File

@ -29,14 +29,38 @@ public class Data {
protected ConcurrentHashMap<String, Object> data = new ConcurrentHashMap();
/**
* Set a value to a specific key
*
* @param key the key
* @param value the value object
* @param type the value type
* @param <T> the value generic
*/
public <T> void set(String key, T value, Class<T> type) {
this.data.put(key, value);
}
/**
* Retrieve a value based on its key
*
* @param key the key
* @param <T> the value type
* @return the data associated with the key
* @throws NullPointerException if the key is not found
*/
public <T> T get(String key) {
return (T) data.get(key);
}
/**
* Retrieve a value based on its key, give a default value if not found
*
* @param key the key
* @param defaultValue the value to return if the key is not found
* @param <T> the value type
* @return {@link #get(String)} if found, {@code defaultValue} otherwise
*/
public <T> T getOrDefault(String key, T defaultValue) {
return (T) data.getOrDefault(key, defaultValue);
}

View File

@ -16,6 +16,17 @@ public class SerializableData extends Data {
private ConcurrentHashMap<String, Class> dataType = new ConcurrentHashMap<>();
/**
* Set a value to a specific key
* <p>
* WARNING: the type needs to be registered in {@link DataManager}
*
* @param key the key
* @param value the value object
* @param type the value type
* @param <T> the value generic
* @throws UnsupportedOperationException if {@code type} is not registered in {@link DataManager}
*/
@Override
public <T> void set(String key, T value, Class<T> type) {
if (DATA_MANAGER.getDataType(type) == null) {
@ -34,6 +45,15 @@ public class SerializableData extends Data {
return data;
}
/**
* Serialize the data into an array of bytes
* <p>
* Use {@link net.minestom.server.reader.DataReader#readData(byte[])}
* to convert it back
*
* @return the array representation of this data object
* @throws IOException if an error occur when serializing the data
*/
public byte[] getSerializedData() throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(output);

View File

@ -929,10 +929,10 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
}
}
Instance instance = getInstance();
final Instance instance = getInstance();
if (instance != null) {
Chunk lastChunk = instance.getChunkAt(lastX, lastZ);
Chunk newChunk = instance.getChunkAt(x, z);
final Chunk lastChunk = instance.getChunkAt(lastX, lastZ);
final Chunk newChunk = instance.getChunkAt(x, z);
if (lastChunk != null && newChunk != null && lastChunk != newChunk) {
synchronized (instance) {
instance.removeEntityFromChunk(this, lastChunk);
@ -952,24 +952,24 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
}
private void updateView(Chunk lastChunk, Chunk newChunk) {
boolean isPlayer = this instanceof Player;
final boolean isPlayer = this instanceof Player;
if (isPlayer)
((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()), MinecraftServer.ENTITY_VIEW_DISTANCE);
long[] updatedVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), MinecraftServer.ENTITY_VIEW_DISTANCE);
final long[] lastVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()), MinecraftServer.ENTITY_VIEW_DISTANCE);
final long[] updatedVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), MinecraftServer.ENTITY_VIEW_DISTANCE);
int[] oldChunksEntity = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunksEntity, updatedVisibleChunksEntity);
final int[] oldChunksEntity = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunksEntity, updatedVisibleChunksEntity);
for (int index : oldChunksEntity) {
int[] chunkPos = ChunkUtils.getChunkCoord(lastVisibleChunksEntity[index]);
Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]);
final int[] chunkPos = ChunkUtils.getChunkCoord(lastVisibleChunksEntity[index]);
final 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;
final Player player = (Player) ent;
if (isAutoViewable())
removeViewer(player);
if (isPlayer) {
@ -981,10 +981,10 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
});
}
int[] newChunksEntity = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunksEntity, lastVisibleChunksEntity);
final int[] newChunksEntity = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunksEntity, lastVisibleChunksEntity);
for (int index : newChunksEntity) {
int[] chunkPos = ChunkUtils.getChunkCoord(updatedVisibleChunksEntity[index]);
Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]);
final int[] chunkPos = ChunkUtils.getChunkCoord(updatedVisibleChunksEntity[index]);
final Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]);
if (chunk == null)
continue;
instance.getChunkEntities(chunk).forEach(ent -> {
@ -1066,12 +1066,12 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
*/
public boolean sameChunk(Position position) {
Check.notNull(position, "Position cannot be null");
Position pos = getPosition();
int chunkX1 = ChunkUtils.getChunkCoordinate((int) Math.floor(pos.getX()));
int chunkZ1 = ChunkUtils.getChunkCoordinate((int) Math.floor(pos.getZ()));
final Position pos = getPosition();
final int chunkX1 = ChunkUtils.getChunkCoordinate((int) Math.floor(pos.getX()));
final int chunkZ1 = ChunkUtils.getChunkCoordinate((int) Math.floor(pos.getZ()));
int chunkX2 = ChunkUtils.getChunkCoordinate((int) Math.floor(position.getX()));
int chunkZ2 = ChunkUtils.getChunkCoordinate((int) Math.floor(position.getZ()));
final int chunkX2 = ChunkUtils.getChunkCoordinate((int) Math.floor(position.getX()));
final int chunkZ2 = ChunkUtils.getChunkCoordinate((int) Math.floor(position.getZ()));
return chunkX1 == chunkX2 && chunkZ1 == chunkZ2;
}

View File

@ -63,24 +63,24 @@ public abstract class EntityCreature extends LivingEntity {
* @param updateView should the entity move its head toward the position?
*/
public void move(float x, float y, float z, boolean updateView) {
Position position = getPosition();
final Position position = getPosition();
Position newPosition = new Position();
// Calculate collisions boxes
onGround = CollisionUtils.handlePhysics(this, new Vector(x, y, z), newPosition, new Vector());
// Refresh target position
float newX = newPosition.getX();
float newY = newPosition.getY();
float newZ = newPosition.getZ();
final float newX = newPosition.getX();
final float newY = newPosition.getY();
final float newZ = newPosition.getZ();
// Creatures cannot move in unload chunk
if (ChunkUtils.isChunkUnloaded(getInstance(), newX, newZ))
return;
float lastYaw = position.getYaw();
float radians = (float) Math.atan2(newZ - position.getZ(), newX - position.getX());
final float lastYaw = position.getYaw();
final float radians = (float) Math.atan2(newZ - position.getZ(), newX - position.getX());
float yaw = (float) (radians * (180.0 / Math.PI)) - 90;
float pitch = position.getPitch(); // TODO
final float yaw = (float) (radians * (180.0 / Math.PI)) - 90;
final float pitch = position.getPitch(); // TODO
final short deltaX = (short) ((newX * 32 - position.getX() * 32) * 128);
final short deltaY = (short) ((newY * 32 - position.getY() * 32) * 128);
@ -289,14 +289,14 @@ public abstract class EntityCreature extends LivingEntity {
* @param speed define how far the entity will move
*/
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);
final float radians = (float) Math.atan2(direction.getZ() - position.getZ(), direction.getX() - position.getX());
final float speedX = (float) (Math.cos(radians) * speed);
final float speedZ = (float) (Math.sin(radians) * speed);
move(speedX, 0, speedZ, true);
}
private void setNextPathPosition() {
BlockPosition blockPosition = blockPositions.pollFirst();
final BlockPosition blockPosition = blockPositions.pollFirst();
if (blockPosition == null) {
this.blockPositions = null;
@ -312,7 +312,7 @@ public abstract class EntityCreature extends LivingEntity {
private void pathProgress() {
if (blockPositions != null) {
if (targetPosition != null) {
float distance = getPosition().getDistance(targetPosition);
final float distance = getPosition().getDistance(targetPosition);
//System.out.println("test: "+distance);
if (distance < 1f) {
setNextPathPosition();

View File

@ -33,7 +33,7 @@ public final class EntityManager {
PlayerLoginEvent loginEvent = new PlayerLoginEvent(playerCache);
playerCache.callEvent(PlayerLoginEvent.class, loginEvent);
Instance spawningInstance = loginEvent.getSpawningInstance();
final Instance spawningInstance = loginEvent.getSpawningInstance();
Check.notNull(spawningInstance, "You need to specify a spawning instance in the PlayerLoginEvent");
@ -68,8 +68,8 @@ public final class EntityManager {
// Add him to the list and change his username/uuid if changed
this.waitingPlayers.add(player);
String username = playerPreLoginEvent.getUsername();
UUID uuid = playerPreLoginEvent.getPlayerUuid();
final String username = playerPreLoginEvent.getUsername();
final UUID uuid = playerPreLoginEvent.getPlayerUuid();
player.setUsername(username);
player.setUuid(uuid);

View File

@ -68,8 +68,8 @@ public class ItemEntity extends ObjectEntity {
(mergeUpdateOption == null || !CooldownUtils.hasCooldown(time, lastMergeCheck, mergeUpdateOption))) {
this.lastMergeCheck = time;
Chunk chunk = instance.getChunkAt(getPosition());
Set<Entity> entities = instance.getChunkEntities(chunk);
final Chunk chunk = instance.getChunkAt(getPosition());
final Set<Entity> entities = instance.getChunkEntities(chunk);
for (Entity entity : entities) {
if (entity instanceof ItemEntity) {

View File

@ -453,7 +453,7 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler {
EntityPropertiesPacket propertiesPacket = new EntityPropertiesPacket();
propertiesPacket.entityId = getEntityId();
int length = Attribute.values().length;
final int length = Attribute.values().length;
EntityPropertiesPacket.Property[] properties = new EntityPropertiesPacket.Property[length];
for (int i = 0; i < length; i++) {
EntityPropertiesPacket.Property property = new EntityPropertiesPacket.Property();

View File

@ -30,7 +30,7 @@ public abstract class ObjectEntity extends Entity {
@Override
public boolean addViewer(Player player) {
boolean result = super.addViewer(player);
final boolean result = super.addViewer(player);
if (!result)
return false;

View File

@ -270,7 +270,7 @@ public class Player extends LivingEntity implements CommandSender {
return false;
// Compute final heart based on health and additional hearts
boolean result = super.damage(type, value);
final boolean result = super.damage(type, value);
if (result) {
lastDamageSource = type;
}
@ -301,7 +301,7 @@ public class Player extends LivingEntity implements CommandSender {
// Target block stage
if (targetCustomBlock != null) {
final byte animationCount = 10;
long since = time - targetBlockTime;
final long since = time - targetBlockTime;
byte stage = (byte) (since / (blockBreakTime / animationCount));
stage = MathUtils.setBetween(stage, (byte) -1, animationCount);
if (stage != targetLastStage) {
@ -315,12 +315,12 @@ public class Player extends LivingEntity implements CommandSender {
}
// Experience orb pickup
Chunk chunk = instance.getChunkAt(getPosition()); // TODO check surrounding chunks
Set<Entity> entities = instance.getChunkEntities(chunk);
final Chunk chunk = instance.getChunkAt(getPosition()); // TODO check surrounding chunks
final Set<Entity> entities = instance.getChunkEntities(chunk);
for (Entity entity : entities) {
if (entity instanceof ExperienceOrb) {
ExperienceOrb experienceOrb = (ExperienceOrb) entity;
BoundingBox itemBoundingBox = experienceOrb.getBoundingBox();
final ExperienceOrb experienceOrb = (ExperienceOrb) entity;
final BoundingBox itemBoundingBox = experienceOrb.getBoundingBox();
if (expandedBoundingBox.intersect(itemBoundingBox)) {
synchronized (experienceOrb) {
if (experienceOrb.shouldRemove() || experienceOrb.isRemoveScheduled())
@ -344,11 +344,11 @@ public class Player extends LivingEntity implements CommandSender {
ItemUpdateStateEvent itemUpdateStateEvent = callItemUpdateStateEvent(true);
// Refresh hand
boolean isOffHand = itemUpdateStateEvent.getHand() == Player.Hand.OFF;
final boolean isOffHand = itemUpdateStateEvent.getHand() == Player.Hand.OFF;
refreshActiveHand(false, isOffHand, false);
ItemStack foodItem = itemUpdateStateEvent.getItemStack();
boolean isFood = foodItem.getMaterial().isFood();
final ItemStack foodItem = itemUpdateStateEvent.getItemStack();
final boolean isFood = foodItem.getMaterial().isFood();
if (isFood) {
PlayerEatEvent playerEatEvent = new PlayerEatEvent(this, foodItem);
@ -508,7 +508,7 @@ public class Player extends LivingEntity implements CommandSender {
if (player == this)
return false;
boolean result = super.addViewer(player);
final boolean result = super.addViewer(player);
if (!result)
return false;
@ -537,7 +537,7 @@ public class Player extends LivingEntity implements CommandSender {
Check.notNull(instance, "instance cannot be null!");
Check.argCondition(this.instance == instance, "Instance should be different than the current one");
boolean firstSpawn = this.instance == null; // TODO: Handle player reconnections, must be false in that case too
final boolean firstSpawn = this.instance == null; // TODO: Handle player reconnections, must be false in that case too
for (Chunk viewableChunk : viewableChunks) {
viewableChunk.removeViewer(this);
}
@ -549,14 +549,14 @@ public class Player extends LivingEntity implements CommandSender {
sendDimension(instanceDimensionType);
}
long[] visibleChunks = ChunkUtils.getChunksInRange(position, getChunkRange());
int length = visibleChunks.length;
final long[] visibleChunks = ChunkUtils.getChunksInRange(position, getChunkRange());
final int length = visibleChunks.length;
AtomicInteger counter = new AtomicInteger(0);
for (int i = 0; i < length; i++) {
int[] chunkPos = ChunkUtils.getChunkCoord(visibleChunks[i]);
int chunkX = chunkPos[0];
int chunkZ = chunkPos[1];
final int[] chunkPos = ChunkUtils.getChunkCoord(visibleChunks[i]);
final int chunkX = chunkPos[0];
final int chunkZ = chunkPos[1];
Consumer<Chunk> callback = (chunk) -> {
if (chunk != null) {
viewableChunks.add(chunk);
@ -564,7 +564,7 @@ public class Player extends LivingEntity implements CommandSender {
instance.sendChunk(this, chunk);
updateViewPosition(chunk);
}
boolean isLast = counter.get() == length - 1;
final boolean isLast = counter.get() == length - 1;
if (isLast) {
// This is the last chunk to be loaded , spawn player
super.setInstance(instance);
@ -606,7 +606,7 @@ public class Player extends LivingEntity implements CommandSender {
/**
* Send a {@link BlockBreakAnimationPacket} packet to the player and his viewers
* Setting {@code destroyStage} to -1 reset the break animation
* Setting {@code destroyStage} to -1 resets the break animation
*
* @param blockPosition the position of the block
* @param destroyStage the destroy stage
@ -899,6 +899,8 @@ public class Player extends LivingEntity implements CommandSender {
/**
* Change the player skin
* <p>
* This does remove the player for all viewers to spawn it again with the correct new skin
*
* @param skin the player skin, null to reset it to his {@link #getUuid()} default skin
*/
@ -908,8 +910,8 @@ public class Player extends LivingEntity implements CommandSender {
DestroyEntitiesPacket destroyEntitiesPacket = new DestroyEntitiesPacket();
destroyEntitiesPacket.entityIds = new int[]{getEntityId()};
PlayerInfoPacket removePlayerPacket = getRemovePlayerToList();
PlayerInfoPacket addPlayerPacket = getAddPlayerToList();
final PlayerInfoPacket removePlayerPacket = getRemovePlayerToList();
final PlayerInfoPacket addPlayerPacket = getAddPlayerToList();
RespawnPacket respawnPacket = new RespawnPacket();
respawnPacket.dimensionType = getDimensionType();
@ -1007,7 +1009,6 @@ public class Player extends LivingEntity implements CommandSender {
public void setResourcePack(ResourcePack resourcePack) {
Check.notNull(resourcePack, "The resource pack cannot be null");
final String url = resourcePack.getUrl();
Check.notNull(url, "The resource pack url cannot be null");
final String hash = resourcePack.getHash();
ResourcePackSendPacket resourcePackSendPacket = new ResourcePackSendPacket();
@ -1157,15 +1158,24 @@ public class Player extends LivingEntity implements CommandSender {
playerConnection.sendPacket(setExperiencePacket);
}
/**
* Called when the player changes chunk (move from one to another)
* <p>
* It does remove and add the player from the chunks viewers list when removed or added
* It also calls the events {@link PlayerChunkUnloadEvent} and {@link PlayerChunkLoadEvent}
*
* @param lastChunk the last player chunk
* @param newChunk the current/new player chunk
*/
protected void onChunkChange(Chunk lastChunk, Chunk newChunk) {
long[] lastVisibleChunks = ChunkUtils.getChunksInRange(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()), MinecraftServer.CHUNK_VIEW_DISTANCE);
long[] updatedVisibleChunks = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), MinecraftServer.CHUNK_VIEW_DISTANCE);
int[] oldChunks = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunks, updatedVisibleChunks);
int[] newChunks = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunks, lastVisibleChunks);
final long[] lastVisibleChunks = ChunkUtils.getChunksInRange(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()), MinecraftServer.CHUNK_VIEW_DISTANCE);
final long[] updatedVisibleChunks = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), MinecraftServer.CHUNK_VIEW_DISTANCE);
final int[] oldChunks = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunks, updatedVisibleChunks);
final int[] newChunks = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunks, lastVisibleChunks);
// Unload old chunks
for (int index : oldChunks) {
int[] chunkPos = ChunkUtils.getChunkCoord(lastVisibleChunks[index]);
final int[] chunkPos = ChunkUtils.getChunkCoord(lastVisibleChunks[index]);
UnloadChunkPacket unloadChunkPacket = new UnloadChunkPacket();
unloadChunkPacket.chunkX = chunkPos[0];
unloadChunkPacket.chunkZ = chunkPos[1];
@ -1180,8 +1190,8 @@ public class Player extends LivingEntity implements CommandSender {
// Load new chunks
for (int i = 0; i < newChunks.length; i++) {
int index = newChunks[i];
int[] chunkPos = ChunkUtils.getChunkCoord(updatedVisibleChunks[index]);
final int index = newChunks[i];
final int[] chunkPos = ChunkUtils.getChunkCoord(updatedVisibleChunks[index]);
instance.loadOptionalChunk(chunkPos[0], chunkPos[1], chunk -> {
if (chunk == null) {
// Cannot load chunk (auto load is not enabled)
@ -1567,7 +1577,8 @@ public class Player extends LivingEntity implements CommandSender {
this.permissionLevel = permissionLevel;
// Magic values: https://wiki.vg/Entity_statuses#Player
byte permissionLevelStatus = (byte) (24 + permissionLevel);
// TODO remove magic values
final byte permissionLevelStatus = (byte) (24 + permissionLevel);
triggerStatus(permissionLevelStatus);
}
@ -1580,7 +1591,8 @@ public class Player extends LivingEntity implements CommandSender {
this.reducedDebugScreenInformation = reduced;
// Magic values: https://wiki.vg/Entity_statuses#Player
byte debugScreenStatus = (byte) (reduced ? 22 : 23);
// TODO remove magic values
final byte debugScreenStatus = (byte) (reduced ? 22 : 23);
triggerStatus(debugScreenStatus);
}

View File

@ -14,8 +14,8 @@ import java.util.Iterator;
*/
public class PlayerSkin {
private String textures;
private String signature;
private final String textures;
private final String signature;
public PlayerSkin(String textures, String signature) {
this.textures = textures;

View File

@ -351,7 +351,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
if (chunk == null)
return new HashSet<>();
long index = ChunkUtils.getChunkIndex(chunk.getChunkX(), chunk.getChunkZ());
final long index = ChunkUtils.getChunkIndex(chunk.getChunkX(), chunk.getChunkZ());
return Collections.unmodifiableSet(getEntitiesInChunk(index));
}

View File

@ -1,15 +1,29 @@
package net.minestom.server.reader;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import net.minestom.server.MinecraftServer;
import net.minestom.server.data.DataManager;
import net.minestom.server.data.SerializableData;
import net.minestom.server.network.packet.PacketReader;
/**
* Class used to convert an array of bytes to a {@link SerializableData}
* <p>
* WARNING: the {@link DataManager} needs to have all the required types as the {@link SerializableData} has
*/
public class DataReader {
private static final DataManager DATA_MANAGER = MinecraftServer.getDataManager();
/**
* Convert a buffer into a {@link SerializableData}
* <p>
* WARNING: the {@link DataManager} needs to have all the required types as the {@link SerializableData} has
*
* @param buffer the data
* @return a {@link SerializableData} based on the data input
*/
public static SerializableData readData(ByteBuf buffer) {
SerializableData data = new SerializableData();
try {
@ -50,4 +64,15 @@ public class DataReader {
return data;
}
/**
* Convert a bytes array to a {@link SerializableData}
*
* @param data the data
* @return a {@link SerializableData} based on the data input
* @see #readData(ByteBuf)
*/
public static SerializableData readData(byte[] data) {
return readData(Unpooled.wrappedBuffer(data));
}
}

View File

@ -1,5 +1,7 @@
package net.minestom.server.resourcepack;
import net.minestom.server.utils.validate.Check;
/**
* Represent a resource pack which can be send to a player
*/
@ -9,6 +11,7 @@ public class ResourcePack {
private String hash;
public ResourcePack(String url, String hash) {
Check.notNull(url, "The resource pack url cannot be null");
this.url = url;
// Optional, set to empty if null
this.hash = hash == null ? "" : hash;