mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-13 19:51:27 +01:00
WIP per block data
This commit is contained in:
parent
be6cdf8e72
commit
972ed294d3
@ -1,6 +1,7 @@
|
||||
package fr.themode.minestom.data;
|
||||
|
||||
import fr.themode.minestom.Main;
|
||||
import fr.themode.minestom.utils.PrimitiveConversion;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
@ -10,13 +11,14 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class Data {
|
||||
|
||||
private DataManager dataManager = Main.getDataManager();
|
||||
private static final DataManager DATA_MANAGER = Main.getDataManager();
|
||||
|
||||
// TODO replace maps for something more memory-friendly
|
||||
private ConcurrentHashMap<String, Object> data = new ConcurrentHashMap();
|
||||
private ConcurrentHashMap<String, Class> dataType = new ConcurrentHashMap<>();
|
||||
|
||||
public <T> void set(String key, T value, Class<T> type) {
|
||||
if (dataManager.getDataType(type) == null) {
|
||||
if (DATA_MANAGER.getDataType(type) == null) {
|
||||
throw new UnsupportedOperationException("Type " + type.getName() + " hasn't been registered in DataManager#registerType");
|
||||
}
|
||||
this.data.put(key, value);
|
||||
@ -41,7 +43,7 @@ public class Data {
|
||||
Object value = entry.getValue();
|
||||
DataType dataType = Main.getDataManager().getDataType(type);
|
||||
|
||||
byte[] encodedType = type.getName().getBytes(); // Data type
|
||||
byte[] encodedType = PrimitiveConversion.getObjectClassString(type.getName()).getBytes(); // Data type (fix for primitives)
|
||||
dos.writeShort(encodedType.length);
|
||||
dos.write(encodedType);
|
||||
|
||||
|
@ -1,10 +1,13 @@
|
||||
package fr.themode.minestom.data;
|
||||
|
||||
import fr.themode.minestom.Main;
|
||||
import fr.themode.minestom.io.DataReader;
|
||||
import fr.themode.minestom.io.IOManager;
|
||||
import fr.themode.minestom.utils.CompressionUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@ -58,46 +61,7 @@ public interface DataContainer {
|
||||
return;
|
||||
}
|
||||
|
||||
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(CompressionUtils.getDecompressedData(array)));
|
||||
|
||||
Data data = new Data();
|
||||
try {
|
||||
while (true) {
|
||||
short typeLength = stream.readShort();
|
||||
|
||||
if (typeLength == 0xff) {
|
||||
// End of data
|
||||
break;
|
||||
}
|
||||
|
||||
byte[] typeCache = new byte[typeLength];
|
||||
for (int i = 0; i < typeLength; i++) {
|
||||
typeCache[i] = stream.readByte();
|
||||
}
|
||||
|
||||
short nameLength = stream.readShort();
|
||||
byte[] nameCache = new byte[nameLength];
|
||||
for (int i = 0; i < nameLength; i++) {
|
||||
nameCache[i] = stream.readByte();
|
||||
}
|
||||
|
||||
int valueLength = stream.readInt();
|
||||
byte[] valueCache = new byte[valueLength];
|
||||
for (int i = 0; i < valueLength; i++) {
|
||||
valueCache[i] = stream.readByte();
|
||||
}
|
||||
|
||||
Class type = Class.forName(new String(typeCache));
|
||||
String name = new String(nameCache);
|
||||
Object value = Main.getDataManager().getDataType(type).decode(valueCache);
|
||||
|
||||
data.set(name, value, type);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
Data data = DataReader.readData(array, true);
|
||||
|
||||
setData(data);
|
||||
if (callback != null)
|
||||
|
@ -6,6 +6,7 @@ import fr.themode.minestom.Main;
|
||||
import fr.themode.minestom.bossbar.BossBar;
|
||||
import fr.themode.minestom.chat.Chat;
|
||||
import fr.themode.minestom.collision.BoundingBox;
|
||||
import fr.themode.minestom.data.Data;
|
||||
import fr.themode.minestom.entity.property.Attribute;
|
||||
import fr.themode.minestom.event.*;
|
||||
import fr.themode.minestom.instance.Chunk;
|
||||
@ -24,6 +25,7 @@ import fr.themode.minestom.utils.*;
|
||||
import fr.themode.minestom.world.Dimension;
|
||||
import fr.themode.minestom.world.LevelType;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
@ -51,8 +53,8 @@ public class Player extends LivingEntity {
|
||||
|
||||
static {
|
||||
ChunkGeneratorDemo chunkGeneratorDemo = new ChunkGeneratorDemo();
|
||||
//instanceContainer = Main.getInstanceManager().createInstanceContainer(new File("C:\\Users\\themo\\OneDrive\\Bureau\\Minestom data"));
|
||||
instanceContainer = Main.getInstanceManager().createInstanceContainer();
|
||||
instanceContainer = Main.getInstanceManager().createInstanceContainer(new File("C:\\Users\\themo\\OneDrive\\Bureau\\Minestom data"));
|
||||
//instanceContainer = Main.getInstanceManager().createInstanceContainer();
|
||||
instanceContainer.enableAutoChunkLoad(true);
|
||||
instanceContainer.setChunkGenerator(chunkGeneratorDemo);
|
||||
int loopStart = -2;
|
||||
@ -133,6 +135,20 @@ public class Player extends LivingEntity {
|
||||
if (player != this)
|
||||
player.teleport(getPosition());
|
||||
}
|
||||
|
||||
getInstance().saveToFolder(() -> {
|
||||
sendMessage("SAVED");
|
||||
});
|
||||
});
|
||||
|
||||
setEventCallback(PlayerStartDiggingEvent.class, event -> {
|
||||
BlockPosition blockPosition = event.getBlockPosition();
|
||||
Data data = getInstance().getBlockData(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
|
||||
if (data == null) {
|
||||
sendMessage("DATA NULL");
|
||||
return;
|
||||
}
|
||||
sendMessage("BLOCK DATA: " + data.get("x"));
|
||||
});
|
||||
|
||||
setEventCallback(PickupItemEvent.class, event -> {
|
||||
|
@ -1,15 +1,22 @@
|
||||
package fr.themode.minestom.event;
|
||||
|
||||
import fr.themode.minestom.instance.block.CustomBlock;
|
||||
import fr.themode.minestom.utils.BlockPosition;
|
||||
|
||||
public class PlayerStartDiggingEvent extends CancellableEvent {
|
||||
|
||||
private BlockPosition blockPosition;
|
||||
private CustomBlock customBlock;
|
||||
|
||||
public PlayerStartDiggingEvent(CustomBlock customBlock) {
|
||||
public PlayerStartDiggingEvent(BlockPosition blockPosition, CustomBlock customBlock) {
|
||||
this.blockPosition = blockPosition;
|
||||
this.customBlock = customBlock;
|
||||
}
|
||||
|
||||
public BlockPosition getBlockPosition() {
|
||||
return blockPosition;
|
||||
}
|
||||
|
||||
public CustomBlock getBlock() {
|
||||
return customBlock;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package fr.themode.minestom.instance;
|
||||
|
||||
import fr.themode.minestom.Main;
|
||||
import fr.themode.minestom.data.Data;
|
||||
import fr.themode.minestom.instance.block.BlockManager;
|
||||
import fr.themode.minestom.instance.block.CustomBlock;
|
||||
import fr.themode.minestom.utils.BlockPosition;
|
||||
@ -10,9 +11,17 @@ public interface BlockModifier {
|
||||
|
||||
BlockManager BLOCK_MANAGER = Main.getBlockManager();
|
||||
|
||||
void setBlock(int x, int y, int z, short blockId);
|
||||
void setBlock(int x, int y, int z, short blockId, Data data);
|
||||
|
||||
void setCustomBlock(int x, int y, int z, short blockId);
|
||||
void setCustomBlock(int x, int y, int z, short blockId, Data data);
|
||||
|
||||
default void setBlock(int x, int y, int z, short blockId) {
|
||||
setBlock(x, y, z, blockId, null);
|
||||
}
|
||||
|
||||
default void setCustomBlock(int x, int y, int z, short blockId) {
|
||||
setCustomBlock(x, y, z, blockId, null);
|
||||
}
|
||||
|
||||
default void setBlock(BlockPosition blockPosition, short blockId) {
|
||||
setBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), blockId);
|
||||
@ -22,9 +31,13 @@ public interface BlockModifier {
|
||||
setBlock(position.toBlockPosition(), blockId);
|
||||
}
|
||||
|
||||
default void setCustomBlock(int x, int y, int z, String blockId) {
|
||||
default void setCustomBlock(int x, int y, int z, String blockId, Data data) {
|
||||
CustomBlock customBlock = BLOCK_MANAGER.getBlock(blockId);
|
||||
setCustomBlock(x, y, z, customBlock.getId());
|
||||
setCustomBlock(x, y, z, customBlock.getId(), data);
|
||||
}
|
||||
|
||||
default void setCustomBlock(int x, int y, int z, String blockId) {
|
||||
setCustomBlock(x, y, z, blockId, null);
|
||||
}
|
||||
|
||||
default void setCustomBlock(BlockPosition blockPosition, String blockId) {
|
||||
|
@ -12,6 +12,8 @@ import fr.themode.minestom.utils.PacketUtils;
|
||||
import fr.themode.minestom.utils.SerializerUtils;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
@ -31,6 +33,16 @@ public class Chunk implements Viewable {
|
||||
|
||||
private Biome biome;
|
||||
private int chunkX, chunkZ;
|
||||
|
||||
// Int represent the chunk coord of the block
|
||||
// value is: 2 bytes -> blockId | 2 bytes -> customBlockId (filled with 0 if isn't)
|
||||
private Int2IntMap blocks = new Int2IntOpenHashMap(16 * 16 * 16); // Start with the size of a full chunk section
|
||||
|
||||
// Used to get all blocks with data (no null)
|
||||
// Key is still chunk coord
|
||||
// TODO shouldn't take Data object (too much memory overhead)
|
||||
private Int2ObjectMap<Data> blocksData = new Int2ObjectOpenHashMap<>(16 * 16); // Start with the size of a single row
|
||||
|
||||
protected volatile boolean packetUpdated;
|
||||
|
||||
// Block entities
|
||||
@ -41,9 +53,6 @@ public class Chunk implements Viewable {
|
||||
// Cache
|
||||
private Set<Player> viewers = new CopyOnWriteArraySet<>();
|
||||
private Packet fullDataPacket;
|
||||
// Int represent the chunk coord of the block
|
||||
// value is: 2 bytes -> blockId | 2 bytes -> customBlockId (filled with 0 if isn't)
|
||||
private Int2IntMap blocks = new Int2IntOpenHashMap(16 * 16 * 16); // Start with the size of a full chunk section
|
||||
|
||||
public Chunk(Biome biome, int chunkX, int chunkZ) {
|
||||
this.biome = biome;
|
||||
@ -51,19 +60,35 @@ public class Chunk implements Viewable {
|
||||
this.chunkZ = chunkZ;
|
||||
}
|
||||
|
||||
public void UNSAFE_setBlock(byte x, byte y, byte z, short blockId) {
|
||||
setBlock(x, y, z, blockId, (short) 0);
|
||||
public void UNSAFE_setBlock(byte x, byte y, byte z, short blockId, Data data) {
|
||||
setBlock(x, y, z, blockId, (short) 0, data);
|
||||
}
|
||||
|
||||
public void UNSAFE_setCustomBlock(byte x, byte y, byte z, short customBlockId) {
|
||||
public void UNSAFE_setBlock(byte x, byte y, byte z, short blockId) {
|
||||
UNSAFE_setBlock(x, y, z, blockId, null);
|
||||
}
|
||||
|
||||
public void UNSAFE_setCustomBlock(byte x, byte y, byte z, short customBlockId, Data data) {
|
||||
CustomBlock customBlock = BLOCK_MANAGER.getBlock(customBlockId);
|
||||
if (customBlock == null)
|
||||
throw new IllegalArgumentException("The custom block " + customBlockId + " does not exist or isn't registered");
|
||||
|
||||
setCustomBlock(x, y, z, customBlock);
|
||||
setCustomBlock(x, y, z, customBlock, data);
|
||||
}
|
||||
|
||||
private void setBlock(byte x, byte y, byte z, short blockType, short customId) {
|
||||
public void UNSAFE_setCustomBlock(byte x, byte y, byte z, short customBlockId) {
|
||||
UNSAFE_setCustomBlock(x, y, z, customBlockId, null);
|
||||
}
|
||||
|
||||
private void setCustomBlock(byte x, byte y, byte z, CustomBlock customBlock, Data data) {
|
||||
if (customBlock.hasUpdate()) {
|
||||
Consumer<Data> test = customBlock::update;
|
||||
// TODO add update callback
|
||||
}
|
||||
setBlock(x, y, z, customBlock.getType(), customBlock.getId(), data);
|
||||
}
|
||||
|
||||
private void setBlock(byte x, byte y, byte z, short blockType, short customId, Data data) {
|
||||
int index = SerializerUtils.chunkCoordToIndex(x, y, z);
|
||||
if (blockType != 0 || customId != 0) {
|
||||
int value = (blockType << 16 | customId & 0xFFFF);
|
||||
@ -73,6 +98,13 @@ public class Chunk implements Viewable {
|
||||
this.blocks.remove(index);
|
||||
}
|
||||
|
||||
// Set the new data (or remove from the map if is null)
|
||||
if (data != null) {
|
||||
this.blocksData.put(index, data);
|
||||
} else {
|
||||
this.blocksData.remove(index);
|
||||
}
|
||||
|
||||
if (isBlockEntity(blockType)) {
|
||||
this.blockEntities.add(index);
|
||||
} else {
|
||||
@ -82,12 +114,13 @@ public class Chunk implements Viewable {
|
||||
this.packetUpdated = false;
|
||||
}
|
||||
|
||||
private void setCustomBlock(byte x, byte y, byte z, CustomBlock customBlock) {
|
||||
if (customBlock.hasUpdate()) {
|
||||
Consumer<Data> test = customBlock::update;
|
||||
// TODO add update callback
|
||||
public void setBlockData(byte x, byte y, byte z, Data data) {
|
||||
int index = SerializerUtils.chunkCoordToIndex(x, y, z);
|
||||
if (data != null) {
|
||||
this.blocksData.put(index, data);
|
||||
} else {
|
||||
this.blocksData.remove(index);
|
||||
}
|
||||
setBlock(x, y, z, customBlock.getType(), customBlock.getId());
|
||||
}
|
||||
|
||||
public short getBlockId(byte x, byte y, byte z) {
|
||||
@ -103,6 +136,11 @@ public class Chunk implements Viewable {
|
||||
return id != 0 ? BLOCK_MANAGER.getBlock(id) : null;
|
||||
}
|
||||
|
||||
public Data getData(byte x, byte y, byte z) {
|
||||
int index = SerializerUtils.chunkCoordToIndex(x, y, z);
|
||||
return blocksData.get(index);
|
||||
}
|
||||
|
||||
public void updateBlocks() {
|
||||
/**
|
||||
* TODO blocks' update:
|
||||
@ -161,9 +199,20 @@ public class Chunk implements Viewable {
|
||||
boolean isCustomBlock = customBlockId != 0;
|
||||
short id = isCustomBlock ? customBlockId : blockId;
|
||||
|
||||
Data data = blocksData.get(index);
|
||||
boolean hasData = data != null;
|
||||
|
||||
dos.writeInt(index); // Chunk coord
|
||||
dos.writeBoolean(isCustomBlock); // Determine the type of the ID
|
||||
dos.writeShort(id);
|
||||
|
||||
dos.writeBoolean(hasData);
|
||||
if (hasData) {
|
||||
byte[] d = data.getSerializedData();
|
||||
dos.writeInt(d.length);
|
||||
dos.write(d);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
byte[] result = output.toByteArray();
|
||||
|
@ -1,11 +1,13 @@
|
||||
package fr.themode.minestom.instance;
|
||||
|
||||
import fr.themode.minestom.instance.batch.ChunkBatch;
|
||||
import fr.themode.minestom.io.ChunkReader;
|
||||
import fr.themode.minestom.io.IOManager;
|
||||
import fr.themode.minestom.utils.CompressionUtils;
|
||||
import fr.themode.minestom.utils.SerializerUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@ -53,37 +55,7 @@ public class ChunkLoaderIO {
|
||||
return;
|
||||
}
|
||||
|
||||
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(CompressionUtils.getDecompressedData(array)));
|
||||
|
||||
ChunkBatch chunkBatch = null;
|
||||
try {
|
||||
Biome biome = Biome.fromId(stream.readByte());
|
||||
Chunk chunk = new Chunk(biome, chunkX, chunkZ);
|
||||
|
||||
chunkBatch = instance.createChunkBatch(chunk);
|
||||
|
||||
while (true) {
|
||||
// TODO block data
|
||||
int index = stream.readInt();
|
||||
boolean isCustomBlock = stream.readBoolean();
|
||||
short blockId = stream.readShort();
|
||||
|
||||
byte[] chunkPos = SerializerUtils.indexToChunkPosition(index);
|
||||
byte x = chunkPos[0];
|
||||
byte y = chunkPos[1];
|
||||
byte z = chunkPos[2];
|
||||
if (isCustomBlock) {
|
||||
chunkBatch.setCustomBlock(x, y, z, blockId);
|
||||
} else {
|
||||
chunkBatch.setBlock(x, y, z, blockId);
|
||||
}
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
chunkBatch.flush(c -> callback.accept(c)); // Success, null if file isn't properly encoded
|
||||
ChunkReader.readChunk(array, instance, chunkX, chunkZ, true, callback);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package fr.themode.minestom.instance;
|
||||
|
||||
import com.github.simplenet.packet.Packet;
|
||||
import fr.themode.minestom.Main;
|
||||
import fr.themode.minestom.data.Data;
|
||||
import fr.themode.minestom.entity.*;
|
||||
import fr.themode.minestom.instance.batch.BlockBatch;
|
||||
import fr.themode.minestom.instance.batch.ChunkBatch;
|
||||
@ -147,6 +148,11 @@ public abstract class Instance implements BlockModifier {
|
||||
return chunk.getCustomBlock((byte) (x % 16), (byte) y, (byte) (z % 16));
|
||||
}
|
||||
|
||||
public Data getBlockData(int x, int y, int z) {
|
||||
Chunk chunk = getChunkAt(x, z);
|
||||
return chunk.getData((byte) (x % 16), (byte) y, (byte) (z % 16));
|
||||
}
|
||||
|
||||
public Chunk getChunkAt(double x, double z) {
|
||||
int chunkX = Math.floorDiv((int) x, 16);
|
||||
int chunkZ = Math.floorDiv((int) z, 16);
|
||||
|
@ -1,6 +1,7 @@
|
||||
package fr.themode.minestom.instance;
|
||||
|
||||
import com.github.simplenet.packet.Packet;
|
||||
import fr.themode.minestom.data.Data;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.event.PlayerBlockBreakEvent;
|
||||
import fr.themode.minestom.instance.batch.BlockBatch;
|
||||
@ -37,25 +38,25 @@ public class InstanceContainer extends Instance {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setBlock(int x, int y, int z, short blockId) {
|
||||
public synchronized void setBlock(int x, int y, int z, short blockId, Data data) {
|
||||
Chunk chunk = getChunkAt(x, z);
|
||||
synchronized (chunk) {
|
||||
byte chunkX = (byte) (x % 16);
|
||||
byte chunkY = (byte) y;
|
||||
byte chunkZ = (byte) (z % 16);
|
||||
chunk.UNSAFE_setBlock(chunkX, chunkY, chunkZ, blockId);
|
||||
chunk.UNSAFE_setBlock(chunkX, chunkY, chunkZ, blockId, data);
|
||||
sendBlockChange(chunk, x, y, z, blockId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setCustomBlock(int x, int y, int z, short blockId) {
|
||||
public synchronized void setCustomBlock(int x, int y, int z, short blockId, Data data) {
|
||||
Chunk chunk = getChunkAt(x, z);
|
||||
synchronized (chunk) {
|
||||
byte chunkX = (byte) (x % 16);
|
||||
byte chunkY = (byte) y;
|
||||
byte chunkZ = (byte) (z % 16);
|
||||
chunk.UNSAFE_setCustomBlock(chunkX, chunkY, chunkZ, blockId);
|
||||
chunk.UNSAFE_setCustomBlock(chunkX, chunkY, chunkZ, blockId, data);
|
||||
short id = BLOCK_MANAGER.getBlock(blockId).getType();
|
||||
sendBlockChange(chunk, x, y, z, id);
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package fr.themode.minestom.instance;
|
||||
|
||||
import fr.themode.minestom.data.Data;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.instance.batch.BlockBatch;
|
||||
import fr.themode.minestom.instance.batch.ChunkBatch;
|
||||
@ -118,13 +119,13 @@ public class SharedInstance extends Instance {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(int x, int y, int z, short blockId) {
|
||||
instanceContainer.setBlock(x, y, z, blockId);
|
||||
public void setBlock(int x, int y, int z, short blockId, Data data) {
|
||||
instanceContainer.setBlock(x, y, z, blockId, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomBlock(int x, int y, int z, short blockId) {
|
||||
instanceContainer.setBlock(x, y, z, blockId);
|
||||
public void setCustomBlock(int x, int y, int z, short blockId, Data data) {
|
||||
instanceContainer.setBlock(x, y, z, blockId, data);
|
||||
}
|
||||
|
||||
public InstanceContainer getContainer() {
|
||||
|
@ -1,5 +1,6 @@
|
||||
package fr.themode.minestom.instance.batch;
|
||||
|
||||
import fr.themode.minestom.data.Data;
|
||||
import fr.themode.minestom.instance.BlockModifier;
|
||||
import fr.themode.minestom.instance.Chunk;
|
||||
import fr.themode.minestom.instance.InstanceContainer;
|
||||
@ -20,36 +21,38 @@ public class BlockBatch implements IBatch, BlockModifier {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void setBlock(int x, int y, int z, short blockId) {
|
||||
public synchronized void setBlock(int x, int y, int z, short blockId, Data data) {
|
||||
Chunk chunk = this.instance.getChunkAt(x, z);
|
||||
List<BlockData> blockData = this.data.getOrDefault(chunk, new ArrayList<>());
|
||||
List<BlockData> blocksData = this.data.getOrDefault(chunk, new ArrayList<>());
|
||||
|
||||
BlockData data = new BlockData();
|
||||
data.x = x % 16;
|
||||
data.y = y;
|
||||
data.z = z % 16;
|
||||
data.blockId = blockId;
|
||||
BlockData blockData = new BlockData();
|
||||
blockData.x = x % 16;
|
||||
blockData.y = y;
|
||||
blockData.z = z % 16;
|
||||
blockData.blockId = blockId;
|
||||
blockData.data = data;
|
||||
|
||||
blockData.add(data);
|
||||
blocksData.add(blockData);
|
||||
|
||||
this.data.put(chunk, blockData);
|
||||
this.data.put(chunk, blocksData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomBlock(int x, int y, int z, short blockId) {
|
||||
public void setCustomBlock(int x, int y, int z, short blockId, Data data) {
|
||||
Chunk chunk = this.instance.getChunkAt(x, z);
|
||||
List<BlockData> blockData = this.data.getOrDefault(chunk, new ArrayList<>());
|
||||
List<BlockData> blocksData = this.data.getOrDefault(chunk, new ArrayList<>());
|
||||
|
||||
BlockData data = new BlockData();
|
||||
data.x = x % 16;
|
||||
data.y = y;
|
||||
data.z = z % 16;
|
||||
data.isCustomBlock = true;
|
||||
data.blockId = blockId;
|
||||
BlockData blockData = new BlockData();
|
||||
blockData.x = x % 16;
|
||||
blockData.y = y;
|
||||
blockData.z = z % 16;
|
||||
blockData.isCustomBlock = true;
|
||||
blockData.blockId = blockId;
|
||||
blockData.data = data;
|
||||
|
||||
blockData.add(data);
|
||||
blocksData.add(blockData);
|
||||
|
||||
this.data.put(chunk, blockData);
|
||||
this.data.put(chunk, blocksData);
|
||||
}
|
||||
|
||||
public void flush(Runnable callback) {
|
||||
@ -81,12 +84,13 @@ public class BlockBatch implements IBatch, BlockModifier {
|
||||
private int x, y, z;
|
||||
private boolean isCustomBlock;
|
||||
private short blockId;
|
||||
private Data data;
|
||||
|
||||
public void apply(Chunk chunk) {
|
||||
if (!isCustomBlock) {
|
||||
chunk.UNSAFE_setBlock((byte) x, (byte) y, (byte) z, blockId);
|
||||
chunk.UNSAFE_setBlock((byte) x, (byte) y, (byte) z, blockId, data);
|
||||
} else {
|
||||
chunk.UNSAFE_setCustomBlock((byte) x, (byte) y, (byte) z, blockId);
|
||||
chunk.UNSAFE_setCustomBlock((byte) x, (byte) y, (byte) z, blockId, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package fr.themode.minestom.instance.batch;
|
||||
|
||||
import fr.themode.minestom.data.Data;
|
||||
import fr.themode.minestom.instance.BlockModifier;
|
||||
import fr.themode.minestom.instance.Chunk;
|
||||
import fr.themode.minestom.instance.ChunkGenerator;
|
||||
@ -25,27 +26,29 @@ public class ChunkBatch implements IBatch, BlockModifier {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(int x, int y, int z, short blockId) {
|
||||
BlockData data = new BlockData();
|
||||
data.x = (byte) x;
|
||||
data.y = (byte) y;
|
||||
data.z = (byte) z;
|
||||
data.isCustomBlock = false;
|
||||
data.blockId = blockId;
|
||||
public void setBlock(int x, int y, int z, short blockId, Data data) {
|
||||
BlockData blockData = new BlockData();
|
||||
blockData.x = (byte) x;
|
||||
blockData.y = (byte) y;
|
||||
blockData.z = (byte) z;
|
||||
blockData.isCustomBlock = false;
|
||||
blockData.blockId = blockId;
|
||||
blockData.data = data;
|
||||
|
||||
this.dataList.add(data);
|
||||
this.dataList.add(blockData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCustomBlock(int x, int y, int z, short blockId) {
|
||||
BlockData data = new BlockData();
|
||||
data.x = (byte) x;
|
||||
data.y = (byte) y;
|
||||
data.z = (byte) z;
|
||||
data.isCustomBlock = true;
|
||||
data.blockId = blockId;
|
||||
public void setCustomBlock(int x, int y, int z, short blockId, Data data) {
|
||||
BlockData blockData = new BlockData();
|
||||
blockData.x = (byte) x;
|
||||
blockData.y = (byte) y;
|
||||
blockData.z = (byte) z;
|
||||
blockData.isCustomBlock = true;
|
||||
blockData.blockId = blockId;
|
||||
blockData.data = data;
|
||||
|
||||
this.dataList.add(data);
|
||||
this.dataList.add(blockData);
|
||||
}
|
||||
|
||||
public void flushChunkGenerator(ChunkGenerator chunkGenerator, Consumer<Chunk> callback) {
|
||||
@ -79,12 +82,13 @@ public class ChunkBatch implements IBatch, BlockModifier {
|
||||
private byte x, y, z;
|
||||
private boolean isCustomBlock;
|
||||
private short blockId;
|
||||
private Data data;
|
||||
|
||||
public void apply(Chunk chunk) {
|
||||
if (!isCustomBlock) {
|
||||
chunk.UNSAFE_setBlock(x, y, z, blockId);
|
||||
chunk.UNSAFE_setBlock(x, y, z, blockId, data);
|
||||
} else {
|
||||
chunk.UNSAFE_setCustomBlock(x, y, z, blockId);
|
||||
chunk.UNSAFE_setCustomBlock(x, y, z, blockId, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package fr.themode.minestom.instance.demo;
|
||||
|
||||
import fr.themode.minestom.data.Data;
|
||||
import fr.themode.minestom.instance.Biome;
|
||||
import fr.themode.minestom.instance.ChunkGenerator;
|
||||
import fr.themode.minestom.instance.batch.ChunkBatch;
|
||||
@ -15,7 +16,9 @@ public class ChunkGeneratorDemo extends ChunkGenerator {
|
||||
for (byte x = 0; x < 16; x++)
|
||||
for (byte z = 0; z < 16; z++) {
|
||||
for (byte y = 0; y < 65; y++) {
|
||||
batch.setCustomBlock(x, y, z, "custom_block");
|
||||
Data data = new Data();
|
||||
data.set("x", (int) x, int.class);
|
||||
batch.setCustomBlock(x, y, z, "custom_block", data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
65
src/main/java/fr/themode/minestom/io/ChunkReader.java
Normal file
65
src/main/java/fr/themode/minestom/io/ChunkReader.java
Normal file
@ -0,0 +1,65 @@
|
||||
package fr.themode.minestom.io;
|
||||
|
||||
import fr.themode.minestom.data.Data;
|
||||
import fr.themode.minestom.instance.Biome;
|
||||
import fr.themode.minestom.instance.Chunk;
|
||||
import fr.themode.minestom.instance.Instance;
|
||||
import fr.themode.minestom.instance.batch.ChunkBatch;
|
||||
import fr.themode.minestom.utils.CompressionUtils;
|
||||
import fr.themode.minestom.utils.SerializerUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ChunkReader {
|
||||
|
||||
public static void readChunk(byte[] b, Instance instance, int chunkX, int chunkZ, boolean shouldDecompress, Consumer<Chunk> callback) {
|
||||
|
||||
b = shouldDecompress ? CompressionUtils.getDecompressedData(b) : b;
|
||||
|
||||
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(b));
|
||||
|
||||
ChunkBatch chunkBatch = null;
|
||||
try {
|
||||
Biome biome = Biome.fromId(stream.readByte());
|
||||
Chunk chunk = new Chunk(biome, chunkX, chunkZ);
|
||||
|
||||
chunkBatch = instance.createChunkBatch(chunk);
|
||||
|
||||
while (true) {
|
||||
// TODO block data
|
||||
int index = stream.readInt();
|
||||
boolean isCustomBlock = stream.readBoolean();
|
||||
short blockId = stream.readShort();
|
||||
boolean hasData = stream.readBoolean();
|
||||
Data data = null;
|
||||
|
||||
// Data deserializer
|
||||
if (hasData) {
|
||||
int dataLength = stream.readInt();
|
||||
byte[] dataArray = stream.readNBytes(dataLength);
|
||||
data = DataReader.readData(dataArray, false);
|
||||
}
|
||||
|
||||
byte[] chunkPos = SerializerUtils.indexToChunkPosition(index);
|
||||
byte x = chunkPos[0];
|
||||
byte y = chunkPos[1];
|
||||
byte z = chunkPos[2];
|
||||
if (isCustomBlock) {
|
||||
chunkBatch.setCustomBlock(x, y, z, blockId, data);
|
||||
} else {
|
||||
chunkBatch.setBlock(x, y, z, blockId, data);
|
||||
}
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
chunkBatch.flush(c -> callback.accept(c)); // Success, null if file isn't properly encoded
|
||||
}
|
||||
|
||||
}
|
61
src/main/java/fr/themode/minestom/io/DataReader.java
Normal file
61
src/main/java/fr/themode/minestom/io/DataReader.java
Normal file
@ -0,0 +1,61 @@
|
||||
package fr.themode.minestom.io;
|
||||
|
||||
import fr.themode.minestom.Main;
|
||||
import fr.themode.minestom.data.Data;
|
||||
import fr.themode.minestom.utils.CompressionUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class DataReader {
|
||||
|
||||
public static Data readData(byte[] b, boolean shouldDecompress) {
|
||||
b = shouldDecompress ? CompressionUtils.getDecompressedData(b) : b;
|
||||
|
||||
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(b));
|
||||
|
||||
Data data = new Data();
|
||||
try {
|
||||
while (true) {
|
||||
short typeLength = stream.readShort();
|
||||
|
||||
if (typeLength == 0xff) {
|
||||
// End of data
|
||||
break;
|
||||
}
|
||||
|
||||
byte[] typeCache = new byte[typeLength];
|
||||
for (int i = 0; i < typeLength; i++) {
|
||||
typeCache[i] = stream.readByte();
|
||||
}
|
||||
|
||||
short nameLength = stream.readShort();
|
||||
byte[] nameCache = new byte[nameLength];
|
||||
for (int i = 0; i < nameLength; i++) {
|
||||
nameCache[i] = stream.readByte();
|
||||
}
|
||||
|
||||
int valueLength = stream.readInt();
|
||||
byte[] valueCache = new byte[valueLength];
|
||||
for (int i = 0; i < valueLength; i++) {
|
||||
valueCache[i] = stream.readByte();
|
||||
}
|
||||
|
||||
Class type = Class.forName(new String(typeCache));
|
||||
|
||||
String name = new String(nameCache);
|
||||
Object value = Main.getDataManager().getDataType(type).decode(valueCache);
|
||||
|
||||
data.set(name, value, type);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
@ -32,7 +32,7 @@ public class PlayerDiggingListener {
|
||||
if (instance != null) {
|
||||
CustomBlock customBlock = instance.getCustomBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
|
||||
if (customBlock != null) {
|
||||
PlayerStartDiggingEvent playerStartDiggingEvent = new PlayerStartDiggingEvent(customBlock);
|
||||
PlayerStartDiggingEvent playerStartDiggingEvent = new PlayerStartDiggingEvent(blockPosition, customBlock);
|
||||
player.callEvent(PlayerStartDiggingEvent.class, playerStartDiggingEvent);
|
||||
if (!playerStartDiggingEvent.isCancelled()) {
|
||||
player.refreshTargetBlock(customBlock, blockPosition);
|
||||
|
@ -41,7 +41,11 @@ public class ChunkDataPacket implements ServerPacket {
|
||||
if (section != null) { // section contains at least one block
|
||||
mask |= 1 << i;
|
||||
Utils.writeBlocks(blocks, section, BITS_PER_ENTRY);
|
||||
} else {
|
||||
mask |= 0 << i;
|
||||
}
|
||||
} else {
|
||||
mask |= 0 << i;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,7 +140,7 @@ public class Position {
|
||||
}
|
||||
|
||||
public BlockPosition toBlockPosition() {
|
||||
return new BlockPosition((int) Math.ceil(getX()), (int) Math.ceil(getY()), (int) Math.ceil(getZ()));
|
||||
return new BlockPosition((int) Math.ceil(x), (int) Math.ceil(y), (int) Math.ceil(z));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -22,4 +22,24 @@ public class PrimitiveConversion {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public static String getObjectClassString(String clazz) {
|
||||
if (clazz == "boolean")
|
||||
return "java.lang.Boolean";
|
||||
if (clazz == "byte")
|
||||
return "java.lang.Byte";
|
||||
if (clazz == "char")
|
||||
return "java.lang.Character";
|
||||
if (clazz == "short")
|
||||
return "java.lang.Short";
|
||||
if (clazz == "int")
|
||||
return "java.lang.Integer";
|
||||
if (clazz == "long")
|
||||
return "java.lang.Long";
|
||||
if (clazz == "float")
|
||||
return "java.lang.Float";
|
||||
if (clazz == "double")
|
||||
return "java.lang.Double";
|
||||
return clazz;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user