Micro optimization + cleanup

This commit is contained in:
themode 2020-08-19 01:24:51 +02:00
parent c5172a7275
commit 8b30caa8c8
54 changed files with 324 additions and 293 deletions

View File

@ -102,8 +102,6 @@ public class MinecraftServer {
//Rate Limiting
private static int rateLimit = 0;
// Networking
private static PacketProcessor packetProcessor;
private static PacketListenerManager packetListenerManager;
private static NettyServer nettyServer;
@ -161,7 +159,8 @@ public class MinecraftServer {
Fluid.values();
connectionManager = new ConnectionManager();
packetProcessor = new PacketProcessor();
// Networking
final PacketProcessor packetProcessor = new PacketProcessor();
packetListenerManager = new PacketListenerManager();
instanceManager = new InstanceManager();

View File

@ -13,6 +13,7 @@ import net.minestom.server.utils.thread.MinestomThread;
import net.minestom.server.utils.validate.Check;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.function.Consumer;
@ -61,7 +62,7 @@ public final class UpdateManager {
//Tick Callbacks
tickStartCallbacks.forEach(Runnable::run);
ArrayList<Future<?>> futures;
List<Future<?>> futures;
// Server tick (instance/chunk/entity)
// Synchronize with the update manager instance, like the signal for chunk load/unload

View File

@ -11,7 +11,7 @@ import java.util.Map;
*/
public class AdvancementManager {
private Map<String, AdvancementTab> advancementTabMap = new HashMap<>();
private final Map<String, AdvancementTab> advancementTabMap = new HashMap<>();
/**
* Create a new tab with a single advancement

View File

@ -16,14 +16,14 @@ import java.util.*;
*/
public class AdvancementTab implements Viewable {
private static Map<Player, Set<AdvancementTab>> playerTabMap = new HashMap<>();
private static final Map<Player, Set<AdvancementTab>> PLAYER_TAB_MAP = new HashMap<>();
private Set<Player> viewers = new HashSet<>();
private final Set<Player> viewers = new HashSet<>();
private AdvancementRoot root;
private final AdvancementRoot root;
// Advancement -> its parent
private Map<Advancement, Advancement> advancementMap = new HashMap<>();
private final Map<Advancement, Advancement> advancementMap = new HashMap<>();
// Packet cache, updated every time the tab changes
protected ByteBuf createBuffer;
@ -47,7 +47,7 @@ public class AdvancementTab implements Viewable {
* @return all the advancement tabs that the player sees
*/
public static Set<AdvancementTab> getTabs(Player player) {
return playerTabMap.getOrDefault(player, null);
return PLAYER_TAB_MAP.getOrDefault(player, null);
}
/**
@ -171,7 +171,7 @@ public class AdvancementTab implements Viewable {
* @param player the player
*/
private void addPlayer(Player player) {
Set<AdvancementTab> tabs = playerTabMap.computeIfAbsent(player, p -> new HashSet<>());
Set<AdvancementTab> tabs = PLAYER_TAB_MAP.computeIfAbsent(player, p -> new HashSet<>());
tabs.add(this);
}
@ -181,13 +181,13 @@ public class AdvancementTab implements Viewable {
* @param player the player
*/
private void removePlayer(Player player) {
if (!playerTabMap.containsKey(player)) {
if (!PLAYER_TAB_MAP.containsKey(player)) {
return;
}
Set<AdvancementTab> tabs = playerTabMap.get(player);
Set<AdvancementTab> tabs = PLAYER_TAB_MAP.get(player);
tabs.remove(this);
if (tabs.isEmpty()) {
playerTabMap.remove(player);
PLAYER_TAB_MAP.remove(player);
}
}

View File

@ -42,7 +42,6 @@ public class BenchmarkManager {
private boolean enabled = false;
private volatile boolean stop = false;
private Thread thread;
private long time;
@ -51,7 +50,7 @@ public class BenchmarkManager {
time = updateOption.getTimeUnit().toMilliseconds(updateOption.getValue());
this.thread = new Thread(null, () -> {
final Thread thread = new Thread(null, () -> {
while (!stop) {
refreshData();
@ -67,7 +66,7 @@ public class BenchmarkManager {
}, MinecraftServer.THREAD_NAME_BENCHMARK, 0L);
this.thread.start();
thread.start();
this.enabled = true;
}

View File

@ -61,7 +61,7 @@ public class ColoredText {
}
private static String toLegacy(String message, char colorChar) {
String result = "";
StringBuilder result = new StringBuilder();
for (int i = 0; i < message.length(); i++) {
final char c = message.charAt(i);
@ -71,19 +71,19 @@ public class ColoredText {
final char nextChar = message.charAt(i + 1);
final ChatColor color = ChatColor.fromLegacyColorCodes(nextChar);
if (color != ChatColor.NO_COLOR) {
String replacement = color.toString();
result += replacement;
final String replacement = color.toString();
result.append(replacement);
i++; // Increment to ignore the color code
} else {
result += c;
result.append(c);
}
}
} else {
result += c;
result.append(c);
}
}
return result;
return result.toString();
}
public ColoredText appendLegacy(String message, char colorChar) {

View File

@ -39,11 +39,11 @@ public class TranslatableText {
final String prefix = "{@";
final String suffix = "}";
String content = code;
StringBuilder content = new StringBuilder(code);
if (arguments != null && arguments.length > 0) {
for (String arg : arguments) {
content += "," + arg;
content.append("," + arg);
}
}

View File

@ -104,37 +104,41 @@ public class CommandDispatcher {
// true if the arg is valid, false otherwise
boolean correct = false;
// the raw string representing the correct argument syntax
String argValue = "";
StringBuilder argValue = new StringBuilder();
if (useRemaining) {
for (int i = argIndex; i < args.length; i++) {
final String arg = args[i];
if (argValue.length() > 0)
argValue += " ";
argValue += arg;
argValue.append(" ");
argValue.append(arg);
}
correctionResult = argument.getCorrectionResult(argValue);
final String argValueString = argValue.toString();
correctionResult = argument.getCorrectionResult(argValueString);
if (correctionResult == Argument.SUCCESS) {
correct = true;
argsValues[argIndex] = argValue;
argsValues[argIndex] = argValueString;
}
} else {
for (int i = argIndex; i < args.length; i++) {
final String arg = args[i];
argValue += arg;
argValue.append(arg);
correctionResult = argument.getCorrectionResult(argValue);
final String argValueString = argValue.toString();
correctionResult = argument.getCorrectionResult(argValueString);
if (correctionResult == Argument.SUCCESS) {
correct = true;
argsValues[argIndex] = argValue;
argsValues[argIndex] = argValueString;
argIndex = i + 1;
break;
} else {
if (!argument.allowSpace())
break;
argValue += " ";
argValue.append(" ");
}
}
}
@ -145,7 +149,7 @@ public class CommandDispatcher {
syntaxCorrect = false;
CommandSuggestionHolder suggestionHolder = new CommandSuggestionHolder();
suggestionHolder.syntax = syntax;
suggestionHolder.argValue = argValue;
suggestionHolder.argValue = argValue.toString();
suggestionHolder.correctionResult = correctionResult;
suggestionHolder.argIndex = argCount;
syntaxesSuggestions.put(argCount, suggestionHolder);
@ -222,7 +226,7 @@ public class CommandDispatcher {
return result;
}
private class CommandSuggestionHolder {
private static class CommandSuggestionHolder {
private CommandSyntax syntax;
private String argValue;
private int correctionResult;
@ -230,7 +234,7 @@ public class CommandDispatcher {
}
private class CommandResult {
private static class CommandResult {
// Command
private Command command;

View File

@ -27,7 +27,7 @@ public class Data {
}
};
protected ConcurrentHashMap<String, Object> data = new ConcurrentHashMap();
protected final ConcurrentHashMap<String, Object> data = new ConcurrentHashMap<>();
/**
* Set a value to a specific key

View File

@ -28,16 +28,15 @@ public final class EntityManager {
private void waitingPlayersTick() {
Player waitingPlayer;
while ((waitingPlayer = waitingPlayers.poll()) != null) {
final Player playerCache = waitingPlayer;
playerCache.init();
waitingPlayer.init();
PlayerLoginEvent loginEvent = new PlayerLoginEvent(playerCache);
playerCache.callEvent(PlayerLoginEvent.class, loginEvent);
PlayerLoginEvent loginEvent = new PlayerLoginEvent(waitingPlayer);
waitingPlayer.callEvent(PlayerLoginEvent.class, loginEvent);
final Instance spawningInstance = loginEvent.getSpawningInstance();
Check.notNull(spawningInstance, "You need to specify a spawning instance in the PlayerLoginEvent");
playerCache.setInstance(spawningInstance);
waitingPlayer.setInstance(spawningInstance);
}
}

View File

@ -35,7 +35,7 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler {
// Bounding box used for items' pickup (see LivingEntity#setBoundingBox)
protected BoundingBox expandedBoundingBox;
private float[] attributeValues = new float[Attribute.values().length];
private final float[] attributeValues = new float[Attribute.values().length];
private boolean isHandActive;
private boolean offHand;

View File

@ -1858,8 +1858,8 @@ public class Player extends LivingEntity implements CommandSender {
if (isFood && !allowFood)
return null;
ItemUpdateStateEvent itemUpdateStateEvent = new ItemUpdateStateEvent(updatedItem,
isOffhand ? Hand.OFF : Hand.MAIN);
final Hand hand = isOffhand ? Hand.OFF : Hand.MAIN;
ItemUpdateStateEvent itemUpdateStateEvent = new ItemUpdateStateEvent(this, hand, updatedItem);
callEvent(ItemUpdateStateEvent.class, itemUpdateStateEvent);
return itemUpdateStateEvent;

View File

@ -17,7 +17,7 @@ public class Hologram implements Viewable {
private static final float OFFSET_Y = -0.9875f;
private HologramEntity entity;
private final HologramEntity entity;
private Position position;
private ColoredText text;
@ -124,7 +124,7 @@ public class Hologram implements Viewable {
}
private class HologramEntity extends EntityArmorStand {
private static class HologramEntity extends EntityArmorStand {
public HologramEntity(Position spawnPosition) {
super(spawnPosition);

View File

@ -7,7 +7,7 @@ import net.minestom.server.instance.block.Block;
public class PFBlockDescription implements IBlockDescription {
private static Short2ObjectMap<PFBlockDescription> blockDescriptionMap = new Short2ObjectOpenHashMap<>();
private static final Short2ObjectMap<PFBlockDescription> BLOCK_DESCRITION_MAP = new Short2ObjectOpenHashMap<>();
/**
* Get the {@link PFBlockDescription} linked to the block state id
@ -18,19 +18,19 @@ public class PFBlockDescription implements IBlockDescription {
* @return the {@link PFBlockDescription} linked to {@code blockStateId}
*/
public static PFBlockDescription getBlockDescription(short blockStateId) {
if (!blockDescriptionMap.containsKey(blockStateId)) {
synchronized (blockDescriptionMap) {
if (!BLOCK_DESCRITION_MAP.containsKey(blockStateId)) {
synchronized (BLOCK_DESCRITION_MAP) {
final Block block = Block.fromStateId(blockStateId);
final PFBlockDescription blockDescription = new PFBlockDescription(block);
blockDescriptionMap.put(blockStateId, blockDescription);
BLOCK_DESCRITION_MAP.put(blockStateId, blockDescription);
return blockDescription;
}
}
return blockDescriptionMap.get(blockStateId);
return BLOCK_DESCRITION_MAP.get(blockStateId);
}
private Block block;
private final Block block;
public PFBlockDescription(Block block) {
this.block = block;

View File

@ -8,7 +8,7 @@ import net.minestom.server.instance.block.Block;
public class PFBlockObject implements IBlockObject {
private static Short2ObjectMap<PFBlockObject> blockObjectsMap = new Short2ObjectOpenHashMap<>();
private static final Short2ObjectMap<PFBlockObject> BLOCK_OBJECT_MAP = new Short2ObjectOpenHashMap<>();
/**
* Get the {@link PFBlockObject} linked to the block state id
@ -19,19 +19,19 @@ public class PFBlockObject implements IBlockObject {
* @return the {@link PFBlockObject} linked to {@code blockStateId}
*/
public static PFBlockObject getBlockObject(short blockStateId) {
if (!blockObjectsMap.containsKey(blockStateId)) {
synchronized (blockObjectsMap) {
if (!BLOCK_OBJECT_MAP.containsKey(blockStateId)) {
synchronized (BLOCK_OBJECT_MAP) {
final Block block = Block.fromStateId(blockStateId);
final PFBlockObject blockObject = new PFBlockObject(block);
blockObjectsMap.put(blockStateId, blockObject);
BLOCK_OBJECT_MAP.put(blockStateId, blockObject);
return blockObject;
}
}
return blockObjectsMap.get(blockStateId);
return BLOCK_OBJECT_MAP.get(blockStateId);
}
private Block block;
private final Block block;
public PFBlockObject(Block block) {
this.block = block;

View File

@ -31,14 +31,11 @@ public class PFInstanceSpace implements IInstanceSpace {
return null;
}
final PFColumnarSpace columnarSpace =
chunkSpaceMap.computeIfAbsent(chunk, c -> {
final PFColumnarSpace cs = new PFColumnarSpace(this, c);
c.setColumnarSpace(cs);
return cs;
});
return columnarSpace;
return chunkSpaceMap.computeIfAbsent(chunk, c -> {
final PFColumnarSpace cs = new PFColumnarSpace(this, c);
c.setColumnarSpace(cs);
return cs;
});
}
public Instance getInstance() {

View File

@ -43,7 +43,7 @@ public interface EventHandler {
* @param <E> the event type
*/
default <E extends Event> void callEvent(Class<E> eventClass, E event) {
List<EventCallback> eventCallbacks = getEventCallbacks(eventClass);
final List<EventCallback> eventCallbacks = getEventCallbacks(eventClass);
for (EventCallback<E> eventCallback : eventCallbacks) {
eventCallback.run(event);
}

View File

@ -8,8 +8,8 @@ import net.minestom.server.instance.Instance;
*/
public class InstanceChunkLoadEvent extends Event {
private Instance instance;
private int chunkX, chunkZ;
private final Instance instance;
private final int chunkX, chunkZ;
public InstanceChunkLoadEvent(Instance instance, int chunkX, int chunkZ) {
this.instance = instance;

View File

@ -8,8 +8,8 @@ import net.minestom.server.instance.Instance;
*/
public class InstanceChunkUnloadEvent extends Event {
private Instance instance;
private int chunkX, chunkZ;
private final Instance instance;
private final int chunkX, chunkZ;
public InstanceChunkUnloadEvent(Instance instance, int chunkX, int chunkZ) {
this.instance = instance;

View File

@ -8,14 +8,15 @@ import net.minestom.server.item.ItemStack;
public class InventoryClickEvent extends Event {
private Player player;
private Inventory inventory;
private int slot;
private ClickType clickType;
private ItemStack clickedItem;
private ItemStack cursorItem;
private final Player player;
private final Inventory inventory;
private final int slot;
private final ClickType clickType;
private final ItemStack clickedItem;
private final ItemStack cursorItem;
public InventoryClickEvent(Player player, Inventory inventory, int slot, ClickType clickType, ItemStack clicked, ItemStack cursor) {
this.player = player;
this.inventory = inventory;
this.slot = slot;
this.clickType = clickType;

View File

@ -6,8 +6,8 @@ import net.minestom.server.inventory.Inventory;
public class InventoryCloseEvent extends Event {
private Player player;
private Inventory inventory;
private final Player player;
private final Inventory inventory;
private Inventory newInventory;
public InventoryCloseEvent(Player player, Inventory inventory) {

View File

@ -6,7 +6,7 @@ import net.minestom.server.inventory.Inventory;
public class InventoryOpenEvent extends CancellableEvent {
private Player player;
private final Player player;
private Inventory inventory;
public InventoryOpenEvent(Player player, Inventory inventory) {

View File

@ -9,10 +9,10 @@ import net.minestom.server.utils.item.ItemStackUtils;
public class InventoryPreClickEvent extends CancellableEvent {
private Player player;
private Inventory inventory;
private int slot;
private ClickType clickType;
private final Player player;
private final Inventory inventory;
private final int slot;
private final ClickType clickType;
private ItemStack clickedItem;
private ItemStack cursorItem;

View File

@ -6,23 +6,29 @@ import net.minestom.server.item.ItemStack;
public class ItemUpdateStateEvent extends Event {
private ItemStack itemStack;
private Player.Hand hand;
private Player player;
private final Player.Hand hand;
private final ItemStack itemStack;
private boolean handAnimation;
public ItemUpdateStateEvent(ItemStack itemStack, Player.Hand hand) {
this.itemStack = itemStack;
public ItemUpdateStateEvent(Player player, Player.Hand hand, ItemStack itemStack) {
this.player = player;
this.hand = hand;
this.itemStack = itemStack;
}
public ItemStack getItemStack() {
return itemStack;
public Player getPlayer() {
return player;
}
public Player.Hand getHand() {
return hand;
}
public ItemStack getItemStack() {
return itemStack;
}
public void setHandAnimation(boolean handAnimation) {
this.handAnimation = handAnimation;
}

View File

@ -28,7 +28,6 @@ import net.minestom.server.world.biomes.Biome;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
// TODO light data & API
@ -46,10 +45,6 @@ public abstract class Chunk implements Viewable {
protected Biome[] biomes;
protected int chunkX, chunkZ;
// blocks id based on coord, see Chunk#getBlockIndex
//public short[] blocksStateId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
//protected short[] customBlocksId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
// Used to get all blocks with data (no null)
// Key is still chunk coord
protected Int2ObjectMap<Data> blocksData = new Int2ObjectOpenHashMap<>(16 * 16); // Start with the size of a single row
@ -59,7 +54,7 @@ public abstract class Chunk implements Viewable {
// (block index)/(last update in ms)
protected Int2LongMap updatableBlocksLastUpdate = new Int2LongOpenHashMap();
protected AtomicBoolean packetUpdated = new AtomicBoolean(false);
protected volatile boolean packetUpdated;
// Block entities
protected Set<Integer> blockEntities = new CopyOnWriteArraySet<>();
@ -114,8 +109,10 @@ public abstract class Chunk implements Viewable {
public abstract CustomBlock getCustomBlock(int x, int y, int z);
protected CustomBlock getCustomBlock(int index) {
final int[] pos = ChunkUtils.indexToChunkPosition(index);
return getCustomBlock(pos[0], pos[1], pos[2]);
final int x = ChunkUtils.blockIndexToChunkPositionX(index);
final int y = ChunkUtils.blockIndexToChunkPositionY(index);
final int z = ChunkUtils.blockIndexToChunkPositionZ(index);
return getCustomBlock(x, y, z);
}
protected abstract void refreshBlockValue(int x, int y, int z, short blockStateId, short customId);
@ -142,7 +139,7 @@ public abstract class Chunk implements Viewable {
return;
// Block all chunk operation during the update
IntIterator iterator = new IntOpenHashSet(updatableBlocks).iterator();
final IntIterator iterator = new IntOpenHashSet(updatableBlocks).iterator();
while (iterator.hasNext()) {
final int index = iterator.nextInt();
final CustomBlock customBlock = getCustomBlock(index);
@ -156,12 +153,7 @@ public abstract class Chunk implements Viewable {
this.updatableBlocksLastUpdate.put(index, time); // Refresh last update time
final int[] blockPos = ChunkUtils.indexToPosition(index, chunkX, chunkZ);
final int x = blockPos[0];
final int y = blockPos[1];
final int z = blockPos[2];
final BlockPosition blockPosition = new BlockPosition(x, y, z);
final BlockPosition blockPosition = ChunkUtils.getBlockPosition(index, chunkX, chunkZ);
final Data data = getData(index);
customBlock.update(instance, blockPosition, data);
}
@ -192,7 +184,7 @@ public abstract class Chunk implements Viewable {
public void setFullDataPacket(ByteBuf fullDataPacket) {
this.fullDataPacket = fullDataPacket;
this.packetUpdated.set(true);
this.packetUpdated = true;
}
protected boolean isBlockEntity(short blockStateId) {
@ -214,13 +206,13 @@ public abstract class Chunk implements Viewable {
}
/**
* Retrieve the updated data packet
* Retrieve (and save if needed) the updated data packet
*
* @param consumer the consumer called once the packet is sure to be up-to-date
*/
public void retrieveDataBuffer(Consumer<ByteBuf> consumer) {
final ByteBuf data = getFullDataPacket();
if (data == null || !packetUpdated.get()) {
if (data == null || !packetUpdated) {
PacketWriterUtils.writeCallbackPacket(getFreshFullDataPacket(), packet -> {
setFullDataPacket(packet);
consumer.accept(packet);
@ -325,9 +317,7 @@ public abstract class Chunk implements Viewable {
final PlayerConnection playerConnection = player.getPlayerConnection();
retrieveDataBuffer(buf -> {
playerConnection.sendPacket(buf, true);
});
retrieveDataBuffer(buf -> playerConnection.sendPacket(buf, true));
// TODO do not hardcode
if (MinecraftServer.isFixLighting()) {
@ -372,15 +362,13 @@ public abstract class Chunk implements Viewable {
public void sendChunkUpdate() {
final Set<Player> chunkViewers = getViewers();
if (!chunkViewers.isEmpty()) {
retrieveDataBuffer(buf -> {
chunkViewers.forEach(player -> {
final PlayerConnection playerConnection = player.getPlayerConnection();
if (!PlayerUtils.isNettyClient(playerConnection))
return;
retrieveDataBuffer(buf -> chunkViewers.forEach(player -> {
final PlayerConnection playerConnection = player.getPlayerConnection();
if (!PlayerUtils.isNettyClient(playerConnection))
return;
playerConnection.sendPacket(buf, true);
});
});
playerConnection.sendPacket(buf, true);
}));
}
}

View File

@ -18,8 +18,9 @@ import java.util.concurrent.CopyOnWriteArraySet;
public class DynamicChunk extends Chunk {
public short[] blocksStateId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
private short[] customBlocksId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
// blocks id based on coordinate, see Chunk#getBlockIndex
private final short[] blocksStateId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
private final short[] customBlocksId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
public DynamicChunk(Biome[] biomes, int chunkX, int chunkZ) {
super(biomes, chunkX, chunkZ);
@ -50,8 +51,7 @@ public class DynamicChunk extends Chunk {
}
final int index = getBlockIndex(x, y, z);
if (blockStateId != 0
|| (blockStateId == 0 && customId != 0 && updateConsumer != null)) { // Allow custom air block for update purpose, refused if no update consumer has been found
if (blockStateId != 0 || customId != 0 && updateConsumer != null) { // Allow custom air block for update purpose, refused if no update consumer has been found
this.blocksStateId[index] = blockStateId;
this.customBlocksId[index] = customId;
} else {
@ -66,7 +66,7 @@ public class DynamicChunk extends Chunk {
this.blockEntities.remove(index);
this.packetUpdated.set(false);
this.packetUpdated = false;
return;
}
@ -93,7 +93,7 @@ public class DynamicChunk extends Chunk {
this.blockEntities.remove(index);
}
this.packetUpdated.set(false);
this.packetUpdated = false;
}
@Override
@ -102,8 +102,7 @@ public class DynamicChunk extends Chunk {
if (!MathUtils.isBetween(index, 0, blocksStateId.length)) {
return 0; // TODO: custom invalid block
}
final short id = blocksStateId[index];
return id;
return blocksStateId[index];
}
@Override
@ -112,8 +111,7 @@ public class DynamicChunk extends Chunk {
if (!MathUtils.isBetween(index, 0, blocksStateId.length)) {
return 0; // TODO: custom invalid block
}
final short id = customBlocksId[index];
return id;
return customBlocksId[index];
}
@Override
@ -179,7 +177,7 @@ public class DynamicChunk extends Chunk {
dos.writeShort(customBlockId);
// Data
final boolean hasData = (data != null && (data instanceof SerializableData));
final boolean hasData = data instanceof SerializableData;
dos.writeBoolean(hasData);
if (hasData) {
final byte[] d = ((SerializableData) data).getSerializedData();
@ -190,8 +188,7 @@ public class DynamicChunk extends Chunk {
}
}
final byte[] result = output.toByteArray();
return result;
return output.toByteArray();
}
@Override

View File

@ -48,9 +48,9 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
private boolean registered;
private DimensionType dimensionType;
private final DimensionType dimensionType;
private WorldBorder worldBorder;
private final WorldBorder worldBorder;
// Tick since the creation of the instance
private long worldAge;
@ -61,24 +61,24 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
private UpdateOption timeUpdate = new UpdateOption(1, TimeUnit.TICK);
private long lastTimeUpdate;
private Map<Class<? extends Event>, List<EventCallback>> eventCallbacks = new ConcurrentHashMap<>();
private final Map<Class<? extends Event>, List<EventCallback>> eventCallbacks = new ConcurrentHashMap<>();
// Entities present in this instance
protected Set<Player> players = new CopyOnWriteArraySet<>();
protected Set<EntityCreature> creatures = new CopyOnWriteArraySet<>();
protected Set<ObjectEntity> objectEntities = new CopyOnWriteArraySet<>();
protected Set<ExperienceOrb> experienceOrbs = new CopyOnWriteArraySet<>();
protected final Set<Player> players = new CopyOnWriteArraySet<>();
protected final Set<EntityCreature> creatures = new CopyOnWriteArraySet<>();
protected final Set<ObjectEntity> objectEntities = new CopyOnWriteArraySet<>();
protected final Set<ExperienceOrb> experienceOrbs = new CopyOnWriteArraySet<>();
// Entities per chunk
protected Map<Long, Set<Entity>> chunkEntities = new ConcurrentHashMap<>();
protected final Map<Long, Set<Entity>> chunkEntities = new ConcurrentHashMap<>();
protected UUID uniqueId;
protected List<Consumer<Instance>> nextTick = Collections.synchronizedList(new ArrayList<>());
protected final List<Consumer<Instance>> nextTick = Collections.synchronizedList(new ArrayList<>());
private Data data;
private ExplosionSupplier explosionSupplier;
// Pathfinder
private PFInstanceSpace instanceSpace = new PFInstanceSpace(this);
private final PFInstanceSpace instanceSpace = new PFInstanceSpace(this);
public Instance(UUID uniqueId, DimensionType dimensionType) {
this.uniqueId = uniqueId;
@ -754,13 +754,13 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
*/
public void removeEntity(Entity entity) {
final Instance entityInstance = entity.getInstance();
if (entityInstance == null || entityInstance != this)
if (entityInstance != this)
return;
RemoveEntityFromInstanceEvent event = new RemoveEntityFromInstanceEvent(this, entity);
callCancellableEvent(RemoveEntityFromInstanceEvent.class, event, () -> {
// Remove this entity from players viewable list and send delete entities packet
entity.getViewers().forEach(p -> entity.removeViewer(p));
entity.getViewers().forEach(entity::removeViewer);
// Remove the entity from cache
final Chunk chunk = getChunkAt(entity.getPosition());
@ -832,8 +832,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
}
private Set<Entity> getEntitiesInChunk(long index) {
final Set<Entity> entities = chunkEntities.computeIfAbsent(index, i -> new CopyOnWriteArraySet<>());
return entities;
return chunkEntities.computeIfAbsent(index, i -> new CopyOnWriteArraySet<>());
}
/**
@ -864,7 +863,7 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
// time
this.worldAge++;
this.time += 1 * timeRate;
this.time += timeRate;
if (timeUpdate != null && !CooldownUtils.hasCooldown(time, lastTimeUpdate, timeUpdate)) {
PacketWriterUtils.writeAndSend(getPlayers(), getTimePacket());

View File

@ -54,8 +54,8 @@ public class InstanceContainer extends Instance {
private ChunkGenerator chunkGenerator;
// WARNING: need to be synchronized properly
private Long2ObjectMap<Chunk> chunks = new Long2ObjectOpenHashMap();
private Set<Chunk> scheduledChunksToRemove = new HashSet<>();
private final Long2ObjectMap<Chunk> chunks = new Long2ObjectOpenHashMap<>();
private final Set<Chunk> scheduledChunksToRemove = new HashSet<>();
private ReadWriteLock changingBlockLock = new ReentrantReadWriteLock();
private Map<BlockPosition, Block> currentlyChangingBlocks = new HashMap<>();
@ -351,8 +351,9 @@ public class InstanceContainer extends Instance {
Check.notNull(getStorageFolder(), "You cannot save the instance if no StorageFolder has been defined");
this.storageFolder.set(UUID_KEY, getUniqueId(), UUID.class);
Data data = getData();
final Data data = getData();
if (data != null) {
// Save the instance data
Check.stateCondition(!(data instanceof SerializableData),
"Instance#getData needs to be a SerializableData in order to be saved");
this.storageFolder.set(DATA_KEY, (SerializableData) getData(), SerializableData.class);

View File

@ -11,7 +11,7 @@ import java.util.function.Consumer;
public class MinestomBasicChunkLoader implements IChunkLoader {
private final static Logger LOGGER = LoggerFactory.getLogger(MinestomBasicChunkLoader.class);
private StorageFolder storageFolder;
private final StorageFolder storageFolder;
public MinestomBasicChunkLoader(StorageFolder storageFolder) {
this.storageFolder = storageFolder;

View File

@ -19,7 +19,7 @@ import java.util.function.Consumer;
*/
public class SharedInstance extends Instance {
private InstanceContainer instanceContainer;
private final InstanceContainer instanceContainer;
public SharedInstance(UUID uniqueId, InstanceContainer instanceContainer) {
super(uniqueId, instanceContainer.getDimensionType());

View File

@ -70,8 +70,10 @@ public class StaticChunk extends Chunk {
fullDataPacket.chunkZ = chunkZ;
short[] blocksStateId = new short[CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z];
for (int i = 0; i < blocksStateId.length; i++) {
final int[] pos = ChunkUtils.indexToPosition(i, 0, 0);
blocksStateId[i] = blockProvider.getBlockStateId(pos[0], pos[1], pos[2]);
final int x = ChunkUtils.blockIndexToChunkPositionX(i);
final int y = ChunkUtils.blockIndexToChunkPositionY(i);
final int z = ChunkUtils.blockIndexToChunkPositionZ(i);
blocksStateId[i] = blockProvider.getBlockStateId(x, y, z);
}
fullDataPacket.blocksStateId = blocksStateId;
fullDataPacket.customBlocksId = new short[0];

View File

@ -11,7 +11,7 @@ import net.minestom.server.utils.Position;
*/
public class WorldBorder {
private Instance instance;
private final Instance instance;
private float centerX, centerZ;

View File

@ -87,7 +87,7 @@ public class BlockBatch implements InstanceBatch {
}
}
private class BlockData {
private static class BlockData {
private int x, y, z;
private boolean hasCustomBlock;

View File

@ -18,8 +18,8 @@ import java.util.function.Consumer;
*/
public class ChunkBatch implements InstanceBatch {
private InstanceContainer instance;
private Chunk chunk;
private final InstanceContainer instance;
private final Chunk chunk;
// Give it the max capacity by default (avoid resizing)
private List<BlockData> dataList =
@ -110,7 +110,7 @@ public class ChunkBatch implements InstanceBatch {
}
}
private class BlockData {
private static class BlockData {
private int x, y, z;
private boolean hasCustomBlock;

View File

@ -27,25 +27,26 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
public class Inventory implements InventoryModifier, InventoryClickHandler, Viewable {
private static volatile byte lastInventoryId;
private static AtomicInteger lastInventoryId = new AtomicInteger();
private byte id;
private final byte id;
private final InventoryType inventoryType;
private String title;
private int size;
private final int size;
private int offset;
private final int offset;
private ItemStack[] itemStacks;
private Set<Player> viewers = new CopyOnWriteArraySet<>();
private ConcurrentHashMap<Player, ItemStack> cursorPlayersItem = new ConcurrentHashMap<>();
private final ItemStack[] itemStacks;
private final Set<Player> viewers = new CopyOnWriteArraySet<>();
private final ConcurrentHashMap<Player, ItemStack> cursorPlayersItem = new ConcurrentHashMap<>();
private List<InventoryCondition> inventoryConditions = new CopyOnWriteArrayList<>();
private InventoryClickProcessor clickProcessor = new InventoryClickProcessor();
private final List<InventoryCondition> inventoryConditions = new CopyOnWriteArrayList<>();
private final InventoryClickProcessor clickProcessor = new InventoryClickProcessor();
// Cached windows packet
@ -64,8 +65,8 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
}
private static byte generateId() {
byte newInventoryId = ++lastInventoryId;
if (newInventoryId < 0)
byte newInventoryId = (byte) lastInventoryId.incrementAndGet();
if (newInventoryId == Byte.MAX_VALUE)
newInventoryId = 1;
return newInventoryId;
}

View File

@ -33,7 +33,7 @@ public enum InventoryProperty {
BREWING_STAND_FUEL_TIME((short) 1);
private short property;
private final short property;
InventoryProperty(short property) {
this.property = property;

View File

@ -26,8 +26,8 @@ public enum InventoryType {
CARTOGRAPHY(21, 3),
STONE_CUTTER(22, 2);
private int windowType;
private int slot;
private final int windowType;
private final int slot;
InventoryType(int windowType, int slot) {
this.windowType = windowType;

View File

@ -8,12 +8,12 @@ import java.util.function.BiConsumer;
public class InventoryClickLoopHandler {
private int start;
private int end;
private int step;
private Int2IntFunction indexModifier;
private Int2ObjectFunction<ItemStack> itemGetter;
private BiConsumer<Integer, ItemStack> itemSetter;
private final int start;
private final int end;
private final int step;
private final Int2IntFunction indexModifier;
private final Int2ObjectFunction<ItemStack> itemGetter;
private final BiConsumer<Integer, ItemStack> itemSetter;
public InventoryClickLoopHandler(int start, int end, int step,
Int2IntFunction indexModifier,

View File

@ -23,8 +23,8 @@ import java.util.function.BiConsumer;
public class InventoryClickProcessor {
// Dragging maps
private Map<Player, IntSet> leftDraggingMap = new HashMap<>();
private Map<Player, IntSet> rightDraggingMap = new HashMap<>();
private final Map<Player, IntSet> leftDraggingMap = new HashMap<>();
private final Map<Player, IntSet> rightDraggingMap = new HashMap<>();
public InventoryClickResult leftClick(Inventory inventory, Player player, int slot, ItemStack clicked, ItemStack cursor) {
final InventoryClickResult clickResult = startCondition(inventory, player, slot, ClickType.LEFT_CLICK, clicked, cursor);

View File

@ -7,12 +7,12 @@ import java.util.UUID;
public class ItemAttribute {
private UUID uuid;
private String internalName;
private Attribute attribute;
private AttributeOperation operation;
private double value;
private AttributeSlot slot;
private final UUID uuid;
private final String internalName;
private final Attribute attribute;
private final AttributeOperation operation;
private final double value;
private final AttributeSlot slot;
public ItemAttribute(UUID uuid, String internalName, Attribute attribute, AttributeOperation operation, double value, AttributeSlot slot) {
this.uuid = uuid;

View File

@ -42,7 +42,7 @@ public class PlayerVehicleListener {
public static void boatSteerListener(ClientSteerBoatPacket packet, Player player) {
final Entity vehicle = player.getVehicle();
if (vehicle == null || !(vehicle instanceof EntityBoat))
if (!(vehicle instanceof EntityBoat))
return;
EntityBoat boat = (EntityBoat) vehicle;

View File

@ -17,8 +17,8 @@ import java.util.UUID;
public class PacketReader extends InputStream {
private ByteBuf buffer;
private NBTReader nbtReader = new NBTReader(this, false);
private final ByteBuf buffer;
private final NBTReader nbtReader = new NBTReader(this, false);
public PacketReader(ByteBuf buffer) {
this.buffer = buffer;

View File

@ -20,8 +20,8 @@ import java.util.function.Consumer;
public class PacketWriter extends OutputStream {
private ByteBuf buffer = Unpooled.buffer();
private NBTWriter nbtWriter = new NBTWriter(this, false);
private final ByteBuf buffer = Unpooled.buffer();
private final NBTWriter nbtWriter = new NBTWriter(this, false);
public void writeBoolean(boolean b) {
buffer.writeBoolean(b);

View File

@ -19,10 +19,10 @@ public class StorageFolder {
private static final DataManager DATA_MANAGER = MinecraftServer.getDataManager();
private StorageSystem storageSystem;
private String folderPath;
private final StorageSystem storageSystem;
private final String folderPath;
private Map<String, SerializableData> cachedData;
private final Map<String, SerializableData> cachedData;
protected StorageFolder(StorageSystem storageSystem, String folderPath, StorageOptions storageOptions) {
this.storageSystem = storageSystem;
@ -50,27 +50,26 @@ public class StorageFolder {
}
public <T> void set(String key, T object, Class<T> type) {
DataType<T> dataType = DATA_MANAGER.getDataType(type);
final DataType<T> dataType = DATA_MANAGER.getDataType(type);
Check.notNull(dataType, "You can only save registered DataType type!");
PacketWriter packetWriter = new PacketWriter();
dataType.encode(packetWriter, object); // Encode
byte[] encodedValue = packetWriter.toByteArray(); // Retrieve bytes
final byte[] encodedValue = packetWriter.toByteArray(); // Retrieve bytes
set(key, encodedValue);
}
public <T> T get(String key, Class<T> type) {
DataType<T> dataType = DATA_MANAGER.getDataType(type);
final DataType<T> dataType = DATA_MANAGER.getDataType(type);
Check.notNull(dataType, "You can only save registered DataType type!");
byte[] data = get(key);
final byte[] data = get(key);
if (data == null)
return null;
PacketReader packetReader = new PacketReader(data);
T value = dataType.decode(packetReader);
return value;
return dataType.decode(packetReader);
}
public <T> T getOrDefault(String key, Class<T> type, T defaultValue) {
@ -95,7 +94,7 @@ public class StorageFolder {
}
// Load it from the storage system
byte[] bytes = get(key);
final byte[] bytes = get(key);
SerializableData data;
if (bytes != null) {
@ -128,7 +127,7 @@ public class StorageFolder {
}
// Load it from the storage system and cache it
byte[] bytes = get(key);
final byte[] bytes = get(key);
SerializableData data;
if (bytes != null) {
@ -151,7 +150,7 @@ public class StorageFolder {
*/
public void saveAndRemoveCachedData(String key) {
synchronized (cachedData) {
SerializableData serializableData = cachedData.get(key);
final SerializableData serializableData = cachedData.get(key);
if (serializableData == null)
return;
@ -173,8 +172,8 @@ public class StorageFolder {
try {
synchronized (cachedData) {
for (Map.Entry<String, SerializableData> entry : cachedData.entrySet()) {
String key = entry.getKey();
SerializableData data = entry.getValue();
final String key = entry.getKey();
final SerializableData data = entry.getValue();
set(key, data.getSerializedData());
}
@ -192,7 +191,7 @@ public class StorageFolder {
public void saveCachedData(String key) {
try {
synchronized (cachedData) {
SerializableData data = cachedData.get(key);
final SerializableData data = cachedData.get(key);
set(key, data.getSerializedData());
}
} catch (IOException e) {

View File

@ -30,9 +30,8 @@ public class StorageManager {
*/
public StorageFolder getFolder(String folderPath, StorageOptions storageOptions, StorageSystem storageSystem) {
Check.notNull(storageOptions, "The storage option cannot be null");
StorageFolder storageFolder =
folderMap.computeIfAbsent(folderPath, s -> new StorageFolder(storageSystem, folderPath, storageOptions));
return storageFolder;
return folderMap.computeIfAbsent(folderPath,
s -> new StorageFolder(storageSystem, folderPath, storageOptions));
}
/**
@ -47,7 +46,7 @@ public class StorageManager {
public StorageFolder getFolder(String folderPath, StorageOptions storageOptions) {
Check.notNull(defaultStorageSystemSupplier,
"You need to either define a default storage system or specify your storage system for this specific folder");
StorageSystem storageSystem = defaultStorageSystemSupplier.get();
final StorageSystem storageSystem = defaultStorageSystemSupplier.get();
return getFolder(folderPath, storageOptions, storageSystem);
}

View File

@ -42,8 +42,7 @@ public class FileStorageSystem implements StorageSystem {
@Override
public byte[] get(String key) {
try {
byte[] result = this.rocksDB.get(getKey(key));
return result;
return rocksDB.get(getKey(key));
} catch (RocksDBException e) {
e.printStackTrace();
return null;

View File

@ -9,8 +9,8 @@ import net.minestom.server.utils.chunk.ChunkUtils;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Separate chunks into group of linked chunks
@ -105,16 +105,16 @@ public class PerGroupChunkProvider extends ThreadProvider {
}
@Override
public ArrayList<Future<?>> update(long time) {
public List<Future<?>> update(long time) {
// Set of already-updated instances this tick
final Set<Instance> updatedInstance = new HashSet<>();
ArrayList<Future<?>> futures = new ArrayList<>();
List<Future<?>> futures = new ArrayList<>();
instanceInstanceMap.forEach((instance, instanceMap) -> {
// True if the instance ended its tick call
AtomicBoolean instanceUpdated = new AtomicBoolean(false);
// True if the instance ended its tick call¬
final CountDownLatch countDownLatch = new CountDownLatch(1);
// Update all the chunks + instances
instanceMap.keySet().forEach(chunksIndexes -> {
@ -124,12 +124,15 @@ public class PerGroupChunkProvider extends ThreadProvider {
// Used to check if the instance has already been updated this tick
if (shouldUpdateInstance) {
updateInstance(instance, time);
instanceUpdated.set(true);
countDownLatch.countDown();
}
// Wait for the instance to be updated
// Needed because the instance tick is used to unload waiting chunks
while (!instanceUpdated.get()) {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
// Tick all this chunk group

View File

@ -7,6 +7,7 @@ import net.minestom.server.utils.chunk.ChunkUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
@ -15,43 +16,43 @@ import java.util.concurrent.Future;
*/
public class PerInstanceThreadProvider extends ThreadProvider {
private Map<Instance, LongSet> instanceChunkMap = new HashMap<>();
private Map<Instance, LongSet> instanceChunkMap = new HashMap<>();
@Override
public void onChunkLoad(Instance instance, int chunkX, int chunkZ) {
// Add the loaded chunk to the instance chunks list
LongSet chunkCoordinates = getChunkCoordinates(instance);
final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
chunkCoordinates.add(index);
}
@Override
public void onChunkLoad(Instance instance, int chunkX, int chunkZ) {
// Add the loaded chunk to the instance chunks list
LongSet chunkCoordinates = getChunkCoordinates(instance);
final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
chunkCoordinates.add(index);
}
@Override
public void onChunkUnload(Instance instance, int chunkX, int chunkZ) {
LongSet chunkCoordinates = getChunkCoordinates(instance);
final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
// Remove the unloaded chunk from the instance list
chunkCoordinates.remove(index);
@Override
public void onChunkUnload(Instance instance, int chunkX, int chunkZ) {
LongSet chunkCoordinates = getChunkCoordinates(instance);
final long index = ChunkUtils.getChunkIndex(chunkX, chunkZ);
// Remove the unloaded chunk from the instance list
chunkCoordinates.remove(index);
}
}
@Override
public ArrayList<Future<?>> update(long time) {
ArrayList<Future<?>> futures = new ArrayList<>();
@Override
public List<Future<?>> update(long time) {
List<Future<?>> futures = new ArrayList<>();
instanceChunkMap.forEach((instance, chunkIndexes) -> {
instanceChunkMap.forEach((instance, chunkIndexes) -> {
futures.add(pool.submit(() -> {
// Tick instance
updateInstance(instance, time);
// Tick chunks
chunkIndexes.forEach((long chunkIndex) -> processChunkTick(instance, chunkIndex, time));
}));
});
return futures;
}
futures.add(pool.submit(() -> {
// Tick instance
updateInstance(instance, time);
// Tick chunks
chunkIndexes.forEach((long chunkIndex) -> processChunkTick(instance, chunkIndex, time));
}));
});
return futures;
}
private LongSet getChunkCoordinates(Instance instance) {
return instanceChunkMap.computeIfAbsent(instance, inst -> new LongArraySet());
}
private LongSet getChunkCoordinates(Instance instance) {
return instanceChunkMap.computeIfAbsent(instance, inst -> new LongArraySet());
}
}

View File

@ -7,7 +7,7 @@ import net.minestom.server.instance.Instance;
import net.minestom.server.utils.chunk.ChunkUtils;
import net.minestom.server.utils.thread.MinestomThread;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
@ -55,9 +55,9 @@ public abstract class ThreadProvider {
* Perform a server tick for all chunks based on their linked thread
*
* @param time the update time in milliseconds
* @return
* @return the futures to execute to complete the tick
*/
public abstract ArrayList<Future<?>> update(long time);
public abstract List<Future<?>> update(long time);
/**
* Get the current size of the thread pool

View File

@ -64,7 +64,7 @@ public final class ArrayUtils {
public static int[] toArray(IntList list) {
int[] array = new int[list.size()];
for (int i = 0; i < array.length; i++) {
array[i] = list.get(i);
array[i] = list.getInt(i);
}
return array;
}

View File

@ -64,11 +64,11 @@ public final class MathUtils {
}
public static int setBetween(int number, int min, int max) {
return number > max ? max : number < min ? min : number;
return number > max ? max : Math.max(number, min);
}
public static float setBetween(float number, float min, float max) {
return number > max ? max : number < min ? min : number;
return number > max ? max : Math.max(number, min);
}
public static int clamp(int value, int min, int max) {

View File

@ -23,21 +23,21 @@ public class PrimitiveConversion {
}
public static String getObjectClassString(String clazz) {
if (clazz == "boolean")
if (clazz.equals("boolean"))
return "java.lang.Boolean";
if (clazz == "byte")
if (clazz.equals("byte"))
return "java.lang.Byte";
if (clazz == "char")
if (clazz.equals("char"))
return "java.lang.Character";
if (clazz == "short")
if (clazz.equals("short"))
return "java.lang.Short";
if (clazz == "int")
if (clazz.equals("int"))
return "java.lang.Integer";
if (clazz == "long")
if (clazz.equals("long"))
return "java.lang.Long";
if (clazz == "float")
if (clazz.equals("float"))
return "java.lang.Float";
if (clazz == "double")
if (clazz.equals("double"))
return "java.lang.Double";
return clazz;
}

View File

@ -41,7 +41,7 @@ public class WeightedRandom<E extends WeightedRandomItem> {
public E get(Random rng) {
final double p = rng.nextDouble() * totalWeight;
for (int i = 0; i < entries.size(); i++) {
final double weightSum = weightSums.get(i);
final double weightSum = weightSums.getDouble(i);
if (weightSum >= p) {
return entries.get(i);
}

View File

@ -85,8 +85,6 @@ public final class ChunkUtils {
* @return the chunk Z based on the index
*/
public static int getChunkCoordZ(long index) {
final int chunkX = (int) (index >> 32);
final int chunkZ = (int) index;
return (int) index;
}
@ -117,8 +115,7 @@ public final class ChunkUtils {
for (int z = startLoop; z < endLoop; z++) {
final int chunkX = getChunkCoordinate((int) (position.getX() + Chunk.CHUNK_SIZE_X * x));
final int chunkZ = getChunkCoordinate((int) (position.getZ() + Chunk.CHUNK_SIZE_Z * z));
visibleChunks[counter] = getChunkIndex(chunkX, chunkZ);
counter++;
visibleChunks[counter++] = getChunkIndex(chunkX, chunkZ);
}
}
return visibleChunks;
@ -130,8 +127,7 @@ public final class ChunkUtils {
* @param instance the instance of the chunks
* @param chunkX the chunk X
* @param chunkZ the chunk Z
* @return an array containing all the loaded neighbours
* can be deserialized using {@link #indexToChunkPosition(int)}
* @return an array containing all the loaded neighbours chunk index
*/
public static long[] getNeighbours(Instance instance, int chunkX, int chunkZ) {
LongList chunks = new LongArrayList();
@ -185,36 +181,76 @@ public final class ChunkUtils {
* @return the instance position of the block located in {@code index}
*/
public static BlockPosition getBlockPosition(int index, int chunkX, int chunkZ) {
final int[] pos = indexToPosition(index, chunkX, chunkZ);
return new BlockPosition(pos[0], pos[1], pos[2]);
final int x = blockIndexToPositionX(index, chunkX);
final int y = blockIndexToPositionY(index);
final int z = blockIndexToPositionZ(index, chunkZ);
return new BlockPosition(x, y, z);
}
/**
* @param index an index computed from {@link #getBlockIndex(int, int, int)}
* Convert a block chunk index to its instance position X
*
* @param index the block chunk index from {@link #getBlockIndex(int, int, int)}
* @param chunkX the chunk X
* @param chunkZ the chunk Z
* @return the world position of the specified index with its chunks being {@code chunkX} and {@code chunk Z}
* positions in the array are in the order X/Y/Z
* @return the X coordinate of the block index
*/
public static int[] indexToPosition(int index, int chunkX, int chunkZ) {
int z = (byte) (index >> 12 & 0xF);
final int y = (index >>> 4 & 0xFF);
// index >> 0 = index
public static int blockIndexToPositionX(int index, int chunkX) {
int x = (byte) (index & 0xF);
x += 16 * chunkX;
z += 16 * chunkZ;
return new int[]{x, y, z};
return x;
}
/**
* @param index an index computed from {@link #getBlockIndex(int, int, int)}
* @return the chunk position (O-15) of the specified index,
* positions in the array are in the order X/Y/Z
* Convert a block chunk index to its instance position Y
*
* @param index the block chunk index from {@link #getBlockIndex(int, int, int)}
* @return the Y coordinate of the block index
*/
public static int[] indexToChunkPosition(int index) {
return indexToPosition(index, 0, 0);
public static int blockIndexToPositionY(int index) {
return (index >>> 4 & 0xFF);
}
/**
* Convert a block chunk index to its instance position Z
*
* @param index the block chunk index from {@link #getBlockIndex(int, int, int)}
* @param chunkZ the chunk Z
* @return the Z coordinate of the block index
*/
public static int blockIndexToPositionZ(int index, int chunkZ) {
int z = (byte) (index >> 12 & 0xF);
z += 16 * chunkZ;
return z;
}
/**
* Convert a block index to a chunk position X
*
* @param index an index computed from {@link #getBlockIndex(int, int, int)}
* @return the chunk position X (O-15) of the specified index
*/
public static int blockIndexToChunkPositionX(int index) {
return blockIndexToPositionX(index, 0);
}
/**
* Convert a block index to a chunk position Y
*
* @param index an index computed from {@link #getBlockIndex(int, int, int)}
* @return the chunk position Y (O-255) of the specified index
*/
public static int blockIndexToChunkPositionY(int index) {
return blockIndexToPositionY(index);
}
/**
* Convert a block index to a chunk position Z
*
* @param index an index computed from {@link #getBlockIndex(int, int, int)}
* @return the chunk position Z (O-15) of the specified index
*/
public static int blockIndexToChunkPositionZ(int index) {
return blockIndexToPositionZ(index, 0);
}
}