mirror of
https://github.com/Minestom/Minestom.git
synced 2024-12-27 03:27:56 +01:00
Basic chunk serializer & reworked chunk multi-threading
This commit is contained in:
parent
d5d3dab6c7
commit
b933a83c31
@ -16,4 +16,5 @@ dependencies {
|
|||||||
testCompile group: 'junit', name: 'junit', version: '4.12'
|
testCompile group: 'junit', name: 'junit', version: '4.12'
|
||||||
implementation 'com.github.Adamaq01:ozao-net:2.3.1'
|
implementation 'com.github.Adamaq01:ozao-net:2.3.1'
|
||||||
compile 'com.github.Querz:NBT:4.1'
|
compile 'com.github.Querz:NBT:4.1'
|
||||||
|
implementation 'com.github.luben:zstd-jni:1.4.3-1'
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,9 @@ public class Main {
|
|||||||
|
|
||||||
// Thread number
|
// Thread number
|
||||||
public static final int THREAD_COUNT_PACKET_WRITER = 3;
|
public static final int THREAD_COUNT_PACKET_WRITER = 3;
|
||||||
|
public static final int THREAD_COUNT_CHUNK_IO = 2;
|
||||||
public static final int THREAD_COUNT_CHUNK_BATCH = 2;
|
public static final int THREAD_COUNT_CHUNK_BATCH = 2;
|
||||||
public static final int THREAD_COUNT_OBJECTS_ENTITIES = 2;
|
public static final int THREAD_COUNT_ENTITIES = 2;
|
||||||
public static final int THREAD_COUNT_CREATURES_ENTITIES = 2;
|
|
||||||
public static final int THREAD_COUNT_PLAYERS_ENTITIES = 2;
|
public static final int THREAD_COUNT_PLAYERS_ENTITIES = 2;
|
||||||
|
|
||||||
public static final int TICK_MS = 50;
|
public static final int TICK_MS = 50;
|
||||||
|
21
src/main/java/fr/themode/minestom/data/Data.java
Normal file
21
src/main/java/fr/themode/minestom/data/Data.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package fr.themode.minestom.data;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class Data {
|
||||||
|
|
||||||
|
private ConcurrentHashMap<String, Object> data = new ConcurrentHashMap();
|
||||||
|
private ConcurrentHashMap<String, DataType> dataType = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
public <T> void set(String key, T value, DataType<T> type) {
|
||||||
|
this.data.put(key, value);
|
||||||
|
this.dataType.put(key, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T get(String key) {
|
||||||
|
return (T) data.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO serialize
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package fr.themode.minestom.data;
|
||||||
|
|
||||||
|
public interface DataContainer {
|
||||||
|
|
||||||
|
Data getData();
|
||||||
|
|
||||||
|
void setData(Data data);
|
||||||
|
|
||||||
|
}
|
15
src/main/java/fr/themode/minestom/data/DataType.java
Normal file
15
src/main/java/fr/themode/minestom/data/DataType.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package fr.themode.minestom.data;
|
||||||
|
|
||||||
|
import fr.themode.minestom.data.type.IntegerData;
|
||||||
|
|
||||||
|
public abstract class DataType<T> {
|
||||||
|
|
||||||
|
public static final DataType INTEGER = new IntegerData();
|
||||||
|
|
||||||
|
public abstract byte[] encode(T value);
|
||||||
|
|
||||||
|
public abstract T decode(byte[] value);
|
||||||
|
|
||||||
|
// TODO get object type class ?
|
||||||
|
|
||||||
|
}
|
17
src/main/java/fr/themode/minestom/data/type/IntegerData.java
Normal file
17
src/main/java/fr/themode/minestom/data/type/IntegerData.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package fr.themode.minestom.data.type;
|
||||||
|
|
||||||
|
import fr.themode.minestom.data.DataType;
|
||||||
|
import fr.themode.minestom.utils.SerializerUtils;
|
||||||
|
|
||||||
|
public class IntegerData extends DataType<Integer> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] encode(Integer value) {
|
||||||
|
return SerializerUtils.intToBytes(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer decode(byte[] value) {
|
||||||
|
return SerializerUtils.bytesToInt(value);
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,8 @@ package fr.themode.minestom.entity;
|
|||||||
import fr.adamaq01.ozao.net.Buffer;
|
import fr.adamaq01.ozao.net.Buffer;
|
||||||
import fr.themode.minestom.Main;
|
import fr.themode.minestom.Main;
|
||||||
import fr.themode.minestom.Viewable;
|
import fr.themode.minestom.Viewable;
|
||||||
|
import fr.themode.minestom.data.Data;
|
||||||
|
import fr.themode.minestom.data.DataContainer;
|
||||||
import fr.themode.minestom.event.Callback;
|
import fr.themode.minestom.event.Callback;
|
||||||
import fr.themode.minestom.event.CancellableEvent;
|
import fr.themode.minestom.event.CancellableEvent;
|
||||||
import fr.themode.minestom.event.Event;
|
import fr.themode.minestom.event.Event;
|
||||||
@ -17,7 +19,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
public abstract class Entity implements Viewable {
|
public abstract class Entity implements Viewable, DataContainer {
|
||||||
|
|
||||||
private static Map<Integer, Entity> entityById = new HashMap<>();
|
private static Map<Integer, Entity> entityById = new HashMap<>();
|
||||||
private static AtomicInteger lastEntityId = new AtomicInteger();
|
private static AtomicInteger lastEntityId = new AtomicInteger();
|
||||||
@ -41,6 +43,7 @@ public abstract class Entity implements Viewable {
|
|||||||
protected Entity vehicle;
|
protected Entity vehicle;
|
||||||
private Map<Class<Event>, Callback> eventCallbacks = new ConcurrentHashMap<>();
|
private Map<Class<Event>, Callback> eventCallbacks = new ConcurrentHashMap<>();
|
||||||
private Set<Player> viewers = new CopyOnWriteArraySet<>();
|
private Set<Player> viewers = new CopyOnWriteArraySet<>();
|
||||||
|
private Data data;
|
||||||
private Set<Entity> passengers = new CopyOnWriteArraySet<>();
|
private Set<Entity> passengers = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
protected UUID uuid;
|
protected UUID uuid;
|
||||||
@ -124,7 +127,20 @@ public abstract class Entity implements Viewable {
|
|||||||
return Collections.unmodifiableSet(viewers);
|
return Collections.unmodifiableSet(viewers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Data getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setData(Data data) {
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
public void tick() {
|
public void tick() {
|
||||||
|
if (instance == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (scheduledRemoveTime != 0) { // Any entity with scheduled remove does not update
|
if (scheduledRemoveTime != 0) { // Any entity with scheduled remove does not update
|
||||||
boolean finished = System.currentTimeMillis() >= scheduledRemoveTime;
|
boolean finished = System.currentTimeMillis() >= scheduledRemoveTime;
|
||||||
if (finished) {
|
if (finished) {
|
||||||
@ -132,10 +148,18 @@ public abstract class Entity implements Viewable {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (shouldUpdate()) {
|
|
||||||
|
if (shouldRemove()) {
|
||||||
|
remove();
|
||||||
|
return;
|
||||||
|
} else if (shouldUpdate()) {
|
||||||
update();
|
update();
|
||||||
this.lastUpdate = System.currentTimeMillis();
|
this.lastUpdate = System.currentTimeMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shouldRemove()) {
|
||||||
|
remove();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public <E extends Event> void setEventCallback(Class<E> eventClass, Callback<E> callback) {
|
public <E extends Event> void setEventCallback(Class<E> eventClass, Callback<E> callback) {
|
||||||
@ -286,6 +310,7 @@ public abstract class Entity implements Viewable {
|
|||||||
synchronized (entityById) {
|
synchronized (entityById) {
|
||||||
entityById.remove(id);
|
entityById.remove(id);
|
||||||
}
|
}
|
||||||
|
instance.removeEntity(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void scheduleRemove(long delay) {
|
public void scheduleRemove(long delay) {
|
||||||
|
@ -6,8 +6,11 @@ import fr.themode.minestom.net.packet.server.play.SpawnMobPacket;
|
|||||||
import fr.themode.minestom.net.player.PlayerConnection;
|
import fr.themode.minestom.net.player.PlayerConnection;
|
||||||
import fr.themode.minestom.utils.Position;
|
import fr.themode.minestom.utils.Position;
|
||||||
|
|
||||||
|
// TODO viewers synchronization each X ticks?
|
||||||
public abstract class EntityCreature extends LivingEntity {
|
public abstract class EntityCreature extends LivingEntity {
|
||||||
|
|
||||||
|
protected boolean isDead;
|
||||||
|
|
||||||
public EntityCreature(int entityType) {
|
public EntityCreature(int entityType) {
|
||||||
super(entityType);
|
super(entityType);
|
||||||
}
|
}
|
||||||
@ -33,6 +36,7 @@ public abstract class EntityCreature extends LivingEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void kill() {
|
public void kill() {
|
||||||
|
this.isDead = true;
|
||||||
triggerStatus((byte) 3);
|
triggerStatus((byte) 3);
|
||||||
scheduleRemove(1000);
|
scheduleRemove(1000);
|
||||||
}
|
}
|
||||||
@ -54,4 +58,8 @@ public abstract class EntityCreature extends LivingEntity {
|
|||||||
playerConnection.sendPacket(spawnMobPacket);
|
playerConnection.sendPacket(spawnMobPacket);
|
||||||
playerConnection.sendPacket(getMetadataPacket());
|
playerConnection.sendPacket(getMetadataPacket());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDead() {
|
||||||
|
return isDead;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
package fr.themode.minestom.entity;
|
package fr.themode.minestom.entity;
|
||||||
|
|
||||||
import fr.themode.minestom.Main;
|
import fr.themode.minestom.Main;
|
||||||
|
import fr.themode.minestom.event.PlayerLoginEvent;
|
||||||
|
import fr.themode.minestom.event.PlayerSpawnPacket;
|
||||||
import fr.themode.minestom.instance.Chunk;
|
import fr.themode.minestom.instance.Chunk;
|
||||||
import fr.themode.minestom.instance.Instance;
|
import fr.themode.minestom.instance.Instance;
|
||||||
import fr.themode.minestom.instance.InstanceManager;
|
import fr.themode.minestom.instance.InstanceManager;
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
@ -13,50 +16,51 @@ public class EntityManager {
|
|||||||
|
|
||||||
private static InstanceManager instanceManager = Main.getInstanceManager();
|
private static InstanceManager instanceManager = Main.getInstanceManager();
|
||||||
|
|
||||||
private ExecutorService objectsPool = Executors.newFixedThreadPool(Main.THREAD_COUNT_OBJECTS_ENTITIES);
|
private ExecutorService entitiesPool = Executors.newFixedThreadPool(Main.THREAD_COUNT_ENTITIES);
|
||||||
private ExecutorService creaturesPool = Executors.newFixedThreadPool(Main.THREAD_COUNT_CREATURES_ENTITIES);
|
|
||||||
private ExecutorService playersPool = Executors.newFixedThreadPool(Main.THREAD_COUNT_PLAYERS_ENTITIES);
|
private ExecutorService playersPool = Executors.newFixedThreadPool(Main.THREAD_COUNT_PLAYERS_ENTITIES);
|
||||||
|
|
||||||
// TODO API for custom thread division (
|
private ConcurrentLinkedQueue<Player> waitingPlayers = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
public void update() {
|
public void update() {
|
||||||
|
waitingPlayersTick();
|
||||||
for (Instance instance : instanceManager.getInstances()) {
|
for (Instance instance : instanceManager.getInstances()) {
|
||||||
testTick2(instance); // TODO optimize update engine for when there are too many entities on one chunk
|
testTick2(instance); // TODO optimize update engine for when there are too many entities on one chunk
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void waitingPlayersTick() {
|
||||||
|
Player waitingPlayer = null;
|
||||||
|
while ((waitingPlayer = waitingPlayers.poll()) != null) {
|
||||||
|
final Player playerCache = waitingPlayer;
|
||||||
|
playersPool.submit(() -> {
|
||||||
|
PlayerLoginEvent loginEvent = new PlayerLoginEvent();
|
||||||
|
playerCache.callEvent(PlayerLoginEvent.class, loginEvent);
|
||||||
|
Instance spawningInstance = loginEvent.getSpawningInstance() == null ? instanceManager.createInstance() : loginEvent.getSpawningInstance();
|
||||||
|
spawningInstance.loadChunk(playerCache.getPosition(), chunk -> {
|
||||||
|
playerCache.spawned = true;
|
||||||
|
playerCache.setInstance(spawningInstance);
|
||||||
|
PlayerSpawnPacket spawnPacket = new PlayerSpawnPacket();
|
||||||
|
playerCache.callEvent(PlayerSpawnPacket.class, spawnPacket);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void testTick2(Instance instance) {
|
private void testTick2(Instance instance) {
|
||||||
|
|
||||||
for (Chunk chunk : instance.getChunks()) {
|
for (Chunk chunk : instance.getChunks()) {
|
||||||
Set<ObjectEntity> objects = chunk.getObjectEntities();
|
Set<ObjectEntity> objects = chunk.getObjectEntities();
|
||||||
Set<EntityCreature> creatures = chunk.getCreatures();
|
Set<EntityCreature> creatures = chunk.getCreatures();
|
||||||
Set<Player> players = chunk.getPlayers();
|
Set<Player> players = chunk.getPlayers();
|
||||||
|
|
||||||
if (!objects.isEmpty()) {
|
if (!creatures.isEmpty() || !objects.isEmpty()) {
|
||||||
objectsPool.submit(() -> {
|
entitiesPool.submit(() -> {
|
||||||
for (ObjectEntity objectEntity : objects) {
|
|
||||||
boolean shouldRemove = objectEntity.shouldRemove();
|
|
||||||
if (!shouldRemove) {
|
|
||||||
objectEntity.tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectEntity.shouldRemove()) {
|
|
||||||
instance.removeEntity(objectEntity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!creatures.isEmpty()) {
|
|
||||||
creaturesPool.submit(() -> {
|
|
||||||
for (EntityCreature creature : creatures) {
|
for (EntityCreature creature : creatures) {
|
||||||
boolean shouldRemove = creature.shouldRemove();
|
|
||||||
if (!shouldRemove) {
|
|
||||||
creature.tick();
|
creature.tick();
|
||||||
}
|
}
|
||||||
|
for (ObjectEntity objectEntity : objects) {
|
||||||
if (creature.shouldRemove()) {
|
objectEntity.tick();
|
||||||
instance.removeEntity(creature);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -64,21 +68,18 @@ public class EntityManager {
|
|||||||
if (!players.isEmpty()) {
|
if (!players.isEmpty()) {
|
||||||
playersPool.submit(() -> {
|
playersPool.submit(() -> {
|
||||||
for (Player player : players) {
|
for (Player player : players) {
|
||||||
boolean shouldRemove = player.shouldRemove();
|
|
||||||
if (!shouldRemove) {
|
|
||||||
player.tick();
|
player.tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player.shouldRemove()) {
|
|
||||||
instance.removeEntity(player);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testTick(Instance instance) {
|
public void addWaitingPlayer(Player player) {
|
||||||
|
this.waitingPlayers.add(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*private void testTick(Instance instance) {
|
||||||
// Creatures
|
// Creatures
|
||||||
for (EntityCreature creature : instance.getCreatures()) {
|
for (EntityCreature creature : instance.getCreatures()) {
|
||||||
creaturesPool.submit(() -> {
|
creaturesPool.submit(() -> {
|
||||||
@ -106,6 +107,6 @@ public class EntityManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ public class ItemEntity extends ObjectEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getData() {
|
public int getObjectData() {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,13 +3,14 @@ package fr.themode.minestom.entity;
|
|||||||
import fr.themode.minestom.net.packet.server.play.SpawnObjectPacket;
|
import fr.themode.minestom.net.packet.server.play.SpawnObjectPacket;
|
||||||
import fr.themode.minestom.net.player.PlayerConnection;
|
import fr.themode.minestom.net.player.PlayerConnection;
|
||||||
|
|
||||||
|
// TODO viewers synchronization each X ticks?
|
||||||
public abstract class ObjectEntity extends Entity {
|
public abstract class ObjectEntity extends Entity {
|
||||||
|
|
||||||
public ObjectEntity(int entityType) {
|
public ObjectEntity(int entityType) {
|
||||||
super(entityType);
|
super(entityType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract int getData();
|
public abstract int getObjectData();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addViewer(Player player) {
|
public void addViewer(Player player) {
|
||||||
@ -21,7 +22,7 @@ public abstract class ObjectEntity extends Entity {
|
|||||||
spawnObjectPacket.uuid = getUuid();
|
spawnObjectPacket.uuid = getUuid();
|
||||||
spawnObjectPacket.type = getEntityType();
|
spawnObjectPacket.type = getEntityType();
|
||||||
spawnObjectPacket.position = getPosition();
|
spawnObjectPacket.position = getPosition();
|
||||||
spawnObjectPacket.data = getData();
|
spawnObjectPacket.data = getObjectData();
|
||||||
playerConnection.sendPacket(spawnObjectPacket);
|
playerConnection.sendPacket(spawnObjectPacket);
|
||||||
playerConnection.sendPacket(getMetadataPacket());
|
playerConnection.sendPacket(getMetadataPacket());
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
package fr.themode.minestom.entity;
|
package fr.themode.minestom.entity;
|
||||||
|
|
||||||
|
import fr.themode.minestom.Main;
|
||||||
import fr.themode.minestom.bossbar.BossBar;
|
import fr.themode.minestom.bossbar.BossBar;
|
||||||
import fr.themode.minestom.chat.Chat;
|
import fr.themode.minestom.chat.Chat;
|
||||||
import fr.themode.minestom.event.AttackEvent;
|
import fr.themode.minestom.entity.demo.ChickenCreature;
|
||||||
import fr.themode.minestom.event.BlockPlaceEvent;
|
import fr.themode.minestom.event.*;
|
||||||
import fr.themode.minestom.event.PickupItemEvent;
|
|
||||||
import fr.themode.minestom.instance.Chunk;
|
import fr.themode.minestom.instance.Chunk;
|
||||||
import fr.themode.minestom.instance.CustomBlock;
|
import fr.themode.minestom.instance.CustomBlock;
|
||||||
|
import fr.themode.minestom.instance.Instance;
|
||||||
|
import fr.themode.minestom.instance.demo.ChunkGeneratorDemo;
|
||||||
import fr.themode.minestom.inventory.Inventory;
|
import fr.themode.minestom.inventory.Inventory;
|
||||||
import fr.themode.minestom.inventory.PlayerInventory;
|
import fr.themode.minestom.inventory.PlayerInventory;
|
||||||
import fr.themode.minestom.item.ItemStack;
|
import fr.themode.minestom.item.ItemStack;
|
||||||
@ -19,6 +21,7 @@ import fr.themode.minestom.utils.Position;
|
|||||||
import fr.themode.minestom.world.Dimension;
|
import fr.themode.minestom.world.Dimension;
|
||||||
import fr.themode.minestom.world.LevelType;
|
import fr.themode.minestom.world.LevelType;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -36,6 +39,8 @@ public class Player extends LivingEntity {
|
|||||||
private Dimension dimension;
|
private Dimension dimension;
|
||||||
private GameMode gameMode;
|
private GameMode gameMode;
|
||||||
private LevelType levelType;
|
private LevelType levelType;
|
||||||
|
// DEBUG
|
||||||
|
private static Instance instance;
|
||||||
private PlayerSettings settings;
|
private PlayerSettings settings;
|
||||||
private PlayerInventory inventory;
|
private PlayerInventory inventory;
|
||||||
private short heldSlot;
|
private short heldSlot;
|
||||||
@ -56,6 +61,23 @@ public class Player extends LivingEntity {
|
|||||||
private float sideways;
|
private float sideways;
|
||||||
private float forward;
|
private float forward;
|
||||||
|
|
||||||
|
static {
|
||||||
|
ChunkGeneratorDemo chunkGeneratorDemo = new ChunkGeneratorDemo();
|
||||||
|
instance = Main.getInstanceManager().createInstance(new File("C:\\Users\\themo\\OneDrive\\Bureau\\Minestom data"));
|
||||||
|
//instance = Main.getInstanceManager().createInstance();
|
||||||
|
instance.setChunkGenerator(chunkGeneratorDemo);
|
||||||
|
int loopStart = -2;
|
||||||
|
int loopEnd = 2;
|
||||||
|
long time = System.currentTimeMillis();
|
||||||
|
for (int x = loopStart; x < loopEnd; x++)
|
||||||
|
for (int z = loopStart; z < loopEnd; z++) {
|
||||||
|
instance.loadChunk(x, z);
|
||||||
|
}
|
||||||
|
System.out.println("Time to load all chunks: " + (System.currentTimeMillis() - time) + " ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean spawned;
|
||||||
|
|
||||||
public Player(UUID uuid, String username, PlayerConnection playerConnection) {
|
public Player(UUID uuid, String username, PlayerConnection playerConnection) {
|
||||||
super(93); // FIXME verify
|
super(93); // FIXME verify
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
@ -96,7 +118,39 @@ public class Player extends LivingEntity {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setEventCallback(BlockPlaceEvent.class, event -> {
|
setEventCallback(BlockPlaceEvent.class, event -> {
|
||||||
sendMessage("Placed block!");
|
/*sendMessage("Placed block! " + event.getHand());
|
||||||
|
Data data = new Data();
|
||||||
|
data.set("test", 5, DataType.INTEGER);
|
||||||
|
setData(data);
|
||||||
|
|
||||||
|
sendMessage("Data: " + getData().get("test"));*/
|
||||||
|
if (event.getHand() != Hand.MAIN)
|
||||||
|
return;
|
||||||
|
|
||||||
|
sendMessage("Save chunk data...");
|
||||||
|
long time = System.currentTimeMillis();
|
||||||
|
getInstance().saveToFolder(() -> {
|
||||||
|
sendMessage("Saved in " + (System.currentTimeMillis() - time) + " ms");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO loginevent set instance
|
||||||
|
setEventCallback(PlayerLoginEvent.class, event -> {
|
||||||
|
System.out.println("PLAYER LOGIN EVENT");
|
||||||
|
event.setSpawningInstance(instance);
|
||||||
|
});
|
||||||
|
|
||||||
|
setEventCallback(PlayerSpawnPacket.class, event -> {
|
||||||
|
System.out.println("TELEPORT");
|
||||||
|
teleport(new Position(0, 66, 0));
|
||||||
|
for (int cx = 0; cx < 4; cx++)
|
||||||
|
for (int cz = 0; cz < 4; cz++) {
|
||||||
|
ChickenCreature chickenCreature = new ChickenCreature();
|
||||||
|
chickenCreature.refreshPosition(0 + (float) cx * 1, 65, 0 + (float) cz * 1);
|
||||||
|
//chickenCreature.setOnFire(true);
|
||||||
|
chickenCreature.setInstance(instance);
|
||||||
|
//chickenCreature.addPassenger(player);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +162,7 @@ public class Player extends LivingEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Target block stage
|
// Target block stage
|
||||||
if (instance != null && targetCustomBlock != null) {
|
if (targetCustomBlock != null) {
|
||||||
int timeBreak = targetCustomBlock.getBreakDelay(this);
|
int timeBreak = targetCustomBlock.getBreakDelay(this);
|
||||||
int animationCount = 10;
|
int animationCount = 10;
|
||||||
long since = System.currentTimeMillis() - targetBlockTime;
|
long since = System.currentTimeMillis() - targetBlockTime;
|
||||||
@ -124,7 +178,6 @@ public class Player extends LivingEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Item pickup
|
// Item pickup
|
||||||
if (instance != null) {
|
|
||||||
Chunk chunk = instance.getChunkAt(getPosition()); // TODO check surrounding chunks
|
Chunk chunk = instance.getChunkAt(getPosition()); // TODO check surrounding chunks
|
||||||
Set<ObjectEntity> objectEntities = chunk.getObjectEntities();
|
Set<ObjectEntity> objectEntities = chunk.getObjectEntities();
|
||||||
for (ObjectEntity objectEntity : objectEntities) {
|
for (ObjectEntity objectEntity : objectEntities) {
|
||||||
@ -154,7 +207,6 @@ public class Player extends LivingEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Multiplayer sync
|
// Multiplayer sync
|
||||||
@ -266,6 +318,14 @@ public class Player extends LivingEntity {
|
|||||||
player.playerConnection.sendPacket(playerInfoPacket);
|
player.playerConnection.sendPacket(playerInfoPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setInstance(Instance instance) {
|
||||||
|
if (!spawned)
|
||||||
|
throw new IllegalStateException("Player#setInstance is only available during and after PlayerSpawnEvent");
|
||||||
|
|
||||||
|
super.setInstance(instance);
|
||||||
|
}
|
||||||
|
|
||||||
public void sendBlockBreakAnimation(BlockPosition blockPosition, byte destroyStage) {
|
public void sendBlockBreakAnimation(BlockPosition blockPosition, byte destroyStage) {
|
||||||
BlockBreakAnimationPacket breakAnimationPacket = new BlockBreakAnimationPacket();
|
BlockBreakAnimationPacket breakAnimationPacket = new BlockBreakAnimationPacket();
|
||||||
breakAnimationPacket.entityId = getEntityId() + 1;
|
breakAnimationPacket.entityId = getEntityId() + 1;
|
||||||
|
@ -13,7 +13,7 @@ public class TestArrow extends ObjectEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getData() {
|
public int getObjectData() {
|
||||||
return shooter.getEntityId() + 1;
|
return shooter.getEntityId() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
20
src/main/java/fr/themode/minestom/event/AnimationEvent.java
Normal file
20
src/main/java/fr/themode/minestom/event/AnimationEvent.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package fr.themode.minestom.event;
|
||||||
|
|
||||||
|
import fr.themode.minestom.entity.Player;
|
||||||
|
|
||||||
|
public class AnimationEvent extends CancellableEvent {
|
||||||
|
|
||||||
|
private Player.Hand hand;
|
||||||
|
|
||||||
|
public AnimationEvent(Player.Hand hand) {
|
||||||
|
this.hand = hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Player.Hand getHand() {
|
||||||
|
return hand;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHand(Player.Hand hand) {
|
||||||
|
this.hand = hand;
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,18 @@
|
|||||||
package fr.themode.minestom.event;
|
package fr.themode.minestom.event;
|
||||||
|
|
||||||
|
import fr.themode.minestom.entity.Player;
|
||||||
import fr.themode.minestom.utils.BlockPosition;
|
import fr.themode.minestom.utils.BlockPosition;
|
||||||
|
|
||||||
public class BlockPlaceEvent extends CancellableEvent {
|
public class BlockPlaceEvent extends CancellableEvent {
|
||||||
|
|
||||||
private short blockId;
|
private short blockId;
|
||||||
private BlockPosition blockPosition;
|
private BlockPosition blockPosition;
|
||||||
|
private Player.Hand hand;
|
||||||
|
|
||||||
public BlockPlaceEvent(short blockId, BlockPosition blockPosition) {
|
public BlockPlaceEvent(short blockId, BlockPosition blockPosition, Player.Hand hand) {
|
||||||
this.blockId = blockId;
|
this.blockId = blockId;
|
||||||
this.blockPosition = blockPosition;
|
this.blockPosition = blockPosition;
|
||||||
|
this.hand = hand;
|
||||||
}
|
}
|
||||||
|
|
||||||
public short getBlockId() {
|
public short getBlockId() {
|
||||||
@ -19,4 +22,8 @@ public class BlockPlaceEvent extends CancellableEvent {
|
|||||||
public BlockPosition getBlockPosition() {
|
public BlockPosition getBlockPosition() {
|
||||||
return blockPosition;
|
return blockPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Player.Hand getHand() {
|
||||||
|
return hand;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,17 @@
|
|||||||
|
package fr.themode.minestom.event;
|
||||||
|
|
||||||
|
import fr.themode.minestom.instance.Instance;
|
||||||
|
|
||||||
|
public class PlayerLoginEvent extends Event {
|
||||||
|
|
||||||
|
private Instance spawningInstance;
|
||||||
|
|
||||||
|
public Instance getSpawningInstance() {
|
||||||
|
return spawningInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSpawningInstance(Instance instance) {
|
||||||
|
this.spawningInstance = instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package fr.themode.minestom.event;
|
||||||
|
|
||||||
|
public class PlayerSpawnPacket extends Event {
|
||||||
|
|
||||||
|
}
|
@ -9,14 +9,20 @@ import fr.themode.minestom.entity.ObjectEntity;
|
|||||||
import fr.themode.minestom.entity.Player;
|
import fr.themode.minestom.entity.Player;
|
||||||
import fr.themode.minestom.net.packet.server.play.ChunkDataPacket;
|
import fr.themode.minestom.net.packet.server.play.ChunkDataPacket;
|
||||||
import fr.themode.minestom.utils.PacketUtils;
|
import fr.themode.minestom.utils.PacketUtils;
|
||||||
|
import fr.themode.minestom.utils.SerializerUtils;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
|
|
||||||
public class Chunk {
|
public class Chunk {
|
||||||
|
|
||||||
private static final int CHUNK_SIZE = 16 * 256 * 16;
|
private static final int CHUNK_SIZE_X = 16;
|
||||||
|
private static final int CHUNK_SIZE_Y = 256;
|
||||||
|
private static final int CHUNK_SIZE_Z = 16;
|
||||||
|
private static final int CHUNK_SIZE = CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z;
|
||||||
|
|
||||||
protected Set<ObjectEntity> objectEntities = new CopyOnWriteArraySet<>();
|
protected Set<ObjectEntity> objectEntities = new CopyOnWriteArraySet<>();
|
||||||
protected Set<EntityCreature> creatures = new CopyOnWriteArraySet<>();
|
protected Set<EntityCreature> creatures = new CopyOnWriteArraySet<>();
|
||||||
@ -52,8 +58,16 @@ public class Chunk {
|
|||||||
setBlock(x, y, z, customBlock.getType(), customBlock.getId());
|
setBlock(x, y, z, customBlock.getType(), customBlock.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void setCustomBlock(byte x, byte y, byte z, short customBlockId) {
|
||||||
|
CustomBlock customBlock = Main.getBlockManager().getBlock(customBlockId);
|
||||||
|
if (customBlock == null)
|
||||||
|
throw new IllegalArgumentException("The custom block " + customBlockId + " does not exist or isn't registered");
|
||||||
|
|
||||||
|
setBlock(x, y, z, customBlock.getType(), customBlockId);
|
||||||
|
}
|
||||||
|
|
||||||
private void setBlock(byte x, byte y, byte z, short blockType, short customId) {
|
private void setBlock(byte x, byte y, byte z, short blockType, short customId) {
|
||||||
int index = getIndex(x, y, z);
|
int index = SerializerUtils.chunkCoordToIndex(x, y, z);
|
||||||
this.blocksId[index] = blockType;
|
this.blocksId[index] = blockType;
|
||||||
this.customBlocks[index] = customId;
|
this.customBlocks[index] = customId;
|
||||||
if (isBlockEntity(blockType)) {
|
if (isBlockEntity(blockType)) {
|
||||||
@ -64,11 +78,11 @@ public class Chunk {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public short getBlockId(byte x, byte y, byte z) {
|
public short getBlockId(byte x, byte y, byte z) {
|
||||||
return this.blocksId[getIndex(x, y, z)];
|
return this.blocksId[SerializerUtils.chunkCoordToIndex(x, y, z)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public CustomBlock getCustomBlock(byte x, byte y, byte z) {
|
public CustomBlock getCustomBlock(byte x, byte y, byte z) {
|
||||||
short id = this.customBlocks[getIndex(x, y, z)];
|
short id = this.customBlocks[SerializerUtils.chunkCoordToIndex(x, y, z)];
|
||||||
return id != 0 ? Main.getBlockManager().getBlock(id) : null;
|
return id != 0 ? Main.getBlockManager().getBlock(id) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,17 +165,55 @@ public class Chunk {
|
|||||||
return blockEntities;
|
return blockEntities;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getIndex(byte x, byte y, byte z) {
|
|
||||||
short index = (short) (x & 0x000F);
|
|
||||||
index |= (y << 4) & 0x0FF0;
|
|
||||||
index |= (z << 12) & 0xF000;
|
|
||||||
return index & 0xffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFullDataPacket(Buffer fullDataPacket) {
|
public void setFullDataPacket(Buffer fullDataPacket) {
|
||||||
this.fullDataPacket = fullDataPacket;
|
this.fullDataPacket = fullDataPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected byte[] getSerializedData() throws IOException {
|
||||||
|
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||||
|
DataOutputStream dos = new DataOutputStream(output);
|
||||||
|
dos.writeByte(biome.getId());
|
||||||
|
|
||||||
|
// TODO customblock id map (StringId -> short id)
|
||||||
|
// TODO List of (sectionId;blockcount;blocktype;blockarray)
|
||||||
|
|
||||||
|
for (byte x = 0; x < CHUNK_SIZE_X; x++) {
|
||||||
|
for (byte y = -128; y < 127; y++) {
|
||||||
|
for (byte z = 0; z < CHUNK_SIZE_Z; z++) {
|
||||||
|
int index = SerializerUtils.chunkCoordToIndex(x, y, z);
|
||||||
|
boolean isCustomBlock = customBlocks[index] != 0;
|
||||||
|
short id = isCustomBlock ? customBlocks[index] : blocksId[index];
|
||||||
|
if (id != 0) {
|
||||||
|
dos.writeInt(index); // Correspond to chunk coord
|
||||||
|
dos.writeBoolean(isCustomBlock);
|
||||||
|
dos.writeShort(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
byte[] result = output.toByteArray();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void loadFromFile(File file) throws IOException {
|
||||||
|
System.out.println("LOAD FROM FILE");
|
||||||
|
byte[] array = Files.readAllBytes(file.toPath());
|
||||||
|
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(array));
|
||||||
|
this.chunkX = stream.readInt();
|
||||||
|
this.chunkZ = stream.readInt();
|
||||||
|
System.out.println("chunk: " + chunkX + " : " + chunkZ);
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
int index = stream.readInt();
|
||||||
|
boolean isCustomBlock = stream.readBoolean();
|
||||||
|
short block = stream.readShort();
|
||||||
|
}
|
||||||
|
} catch (EOFException e) {
|
||||||
|
System.out.println("END");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
protected ChunkDataPacket getFreshFullDataPacket() {
|
protected ChunkDataPacket getFreshFullDataPacket() {
|
||||||
ChunkDataPacket fullDataPacket = new ChunkDataPacket();
|
ChunkDataPacket fullDataPacket = new ChunkDataPacket();
|
||||||
fullDataPacket.chunk = this;
|
fullDataPacket.chunk = this;
|
||||||
|
@ -6,6 +6,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class ChunkBatch implements BlockModifier {
|
public class ChunkBatch implements BlockModifier {
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ public class ChunkBatch implements BlockModifier {
|
|||||||
this.dataList.add(data);
|
this.dataList.add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flush() {
|
public void flush(Consumer<Chunk> callback) {
|
||||||
synchronized (chunk) {
|
synchronized (chunk) {
|
||||||
batchesPool.submit(() -> {
|
batchesPool.submit(() -> {
|
||||||
for (BlockData data : dataList) {
|
for (BlockData data : dataList) {
|
||||||
@ -51,6 +52,8 @@ public class ChunkBatch implements BlockModifier {
|
|||||||
}
|
}
|
||||||
chunk.refreshDataPacket(); // TODO partial refresh instead of full
|
chunk.refreshDataPacket(); // TODO partial refresh instead of full
|
||||||
instance.sendChunkUpdate(chunk); // TODO partial chunk data
|
instance.sendChunkUpdate(chunk); // TODO partial chunk data
|
||||||
|
if (callback != null)
|
||||||
|
callback.accept(chunk);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,85 @@
|
|||||||
|
package fr.themode.minestom.instance;
|
||||||
|
|
||||||
|
import fr.themode.minestom.Main;
|
||||||
|
import fr.themode.minestom.utils.SerializerUtils;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
// TODO compression
|
||||||
|
public class ChunkLoaderIO {
|
||||||
|
|
||||||
|
private ExecutorService chunkLoaderPool = Executors.newFixedThreadPool(Main.THREAD_COUNT_CHUNK_IO);
|
||||||
|
|
||||||
|
private static File getChunkFile(int chunkX, int chunkZ, File folder) {
|
||||||
|
return new File(folder, getChunkFileName(chunkX, chunkZ));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getChunkFileName(int chunkX, int chunkZ) {
|
||||||
|
return "chunk." + chunkX + "." + chunkZ + ".data";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void saveChunk(Chunk chunk, File folder, Runnable callback) {
|
||||||
|
chunkLoaderPool.submit(() -> {
|
||||||
|
File chunkFile = getChunkFile(chunk.getChunkX(), chunk.getChunkZ(), folder);
|
||||||
|
try (FileOutputStream fos = new FileOutputStream(chunkFile)) {
|
||||||
|
byte[] data = chunk.getSerializedData();
|
||||||
|
// Zstd.compress(data, 1)
|
||||||
|
fos.write(data);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (callback != null)
|
||||||
|
callback.run();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void loadChunk(int chunkX, int chunkZ, Instance instance, Consumer<Chunk> callback) {
|
||||||
|
chunkLoaderPool.submit(() -> {
|
||||||
|
File chunkFile = getChunkFile(chunkX, chunkZ, instance.getFolder());
|
||||||
|
if (!chunkFile.exists()) {
|
||||||
|
instance.createChunk(chunkX, chunkZ, callback); // Chunk file does not exist, create new chunk
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] array = new byte[0];
|
||||||
|
try {
|
||||||
|
array = Files.readAllBytes(getChunkFile(chunkX, chunkZ, instance.getFolder()).toPath());
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(array));
|
||||||
|
|
||||||
|
Chunk chunk = null;
|
||||||
|
try {
|
||||||
|
Biome biome = Biome.fromId(stream.readByte());
|
||||||
|
chunk = new Chunk(biome, chunkX, chunkZ);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int index = stream.readInt();
|
||||||
|
boolean isCustomBlock = stream.readBoolean();
|
||||||
|
short blockId = stream.readShort();
|
||||||
|
|
||||||
|
byte[] chunkPos = SerializerUtils.indexToChunkPosition(index);
|
||||||
|
if (isCustomBlock) {
|
||||||
|
chunk.setCustomBlock(chunkPos[0], chunkPos[1], chunkPos[2], blockId);
|
||||||
|
} else {
|
||||||
|
chunk.setBlock(chunkPos[0], chunkPos[1], chunkPos[2], blockId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (EOFException e) {
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
callback.accept(chunk); // Success, null if file isn't properly encoded
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -7,23 +7,24 @@ import fr.themode.minestom.entity.ObjectEntity;
|
|||||||
import fr.themode.minestom.entity.Player;
|
import fr.themode.minestom.entity.Player;
|
||||||
import fr.themode.minestom.event.BlockBreakEvent;
|
import fr.themode.minestom.event.BlockBreakEvent;
|
||||||
import fr.themode.minestom.net.PacketWriter;
|
import fr.themode.minestom.net.PacketWriter;
|
||||||
import fr.themode.minestom.net.packet.server.play.ChunkDataPacket;
|
|
||||||
import fr.themode.minestom.net.packet.server.play.DestroyEntitiesPacket;
|
import fr.themode.minestom.net.packet.server.play.DestroyEntitiesPacket;
|
||||||
import fr.themode.minestom.net.packet.server.play.ParticlePacket;
|
import fr.themode.minestom.net.packet.server.play.ParticlePacket;
|
||||||
import fr.themode.minestom.utils.BlockPosition;
|
import fr.themode.minestom.utils.BlockPosition;
|
||||||
import fr.themode.minestom.utils.GroupedCollections;
|
import fr.themode.minestom.utils.GroupedCollections;
|
||||||
import fr.themode.minestom.utils.Position;
|
import fr.themode.minestom.utils.Position;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.io.File;
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class Instance implements BlockModifier {
|
public class Instance implements BlockModifier {
|
||||||
|
|
||||||
|
private static ChunkLoaderIO chunkLoaderIO = new ChunkLoaderIO();
|
||||||
|
|
||||||
private UUID uniqueId;
|
private UUID uniqueId;
|
||||||
|
private File folder;
|
||||||
|
|
||||||
private GroupedCollections<ObjectEntity> objectEntities = new GroupedCollections<>(new CopyOnWriteArrayList<>());
|
private GroupedCollections<ObjectEntity> objectEntities = new GroupedCollections<>(new CopyOnWriteArrayList<>());
|
||||||
private GroupedCollections<EntityCreature> creatures = new GroupedCollections<>(new CopyOnWriteArrayList());
|
private GroupedCollections<EntityCreature> creatures = new GroupedCollections<>(new CopyOnWriteArrayList());
|
||||||
@ -32,8 +33,9 @@ public class Instance implements BlockModifier {
|
|||||||
private ChunkGenerator chunkGenerator;
|
private ChunkGenerator chunkGenerator;
|
||||||
private Map<Long, Chunk> chunks = new ConcurrentHashMap<>();
|
private Map<Long, Chunk> chunks = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public Instance(UUID uniqueId) {
|
public Instance(UUID uniqueId, File folder) {
|
||||||
this.uniqueId = uniqueId;
|
this.uniqueId = uniqueId;
|
||||||
|
this.folder = folder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -41,10 +43,12 @@ public class Instance implements BlockModifier {
|
|||||||
Chunk chunk = getChunkAt(x, z);
|
Chunk chunk = getChunkAt(x, z);
|
||||||
synchronized (chunk) {
|
synchronized (chunk) {
|
||||||
chunk.setBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId);
|
chunk.setBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId);
|
||||||
PacketWriter.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> {
|
chunk.refreshDataPacket();
|
||||||
|
sendChunkUpdate(chunk);
|
||||||
|
/*PacketWriter.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> {
|
||||||
chunk.setFullDataPacket(buffer);
|
chunk.setFullDataPacket(buffer);
|
||||||
sendChunkUpdate(chunk);
|
sendChunkUpdate(chunk);
|
||||||
});
|
});*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,10 +57,12 @@ public class Instance implements BlockModifier {
|
|||||||
Chunk chunk = getChunkAt(x, z);
|
Chunk chunk = getChunkAt(x, z);
|
||||||
synchronized (chunk) {
|
synchronized (chunk) {
|
||||||
chunk.setBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId);
|
chunk.setBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId);
|
||||||
PacketWriter.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> {
|
chunk.refreshDataPacket();
|
||||||
|
sendChunkUpdate(chunk);
|
||||||
|
/*PacketWriter.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> {
|
||||||
chunk.setFullDataPacket(buffer);
|
chunk.setFullDataPacket(buffer);
|
||||||
sendChunkUpdate(chunk);
|
sendChunkUpdate(chunk);
|
||||||
});
|
});*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,9 +98,28 @@ public class Instance implements BlockModifier {
|
|||||||
breakBlock(player, blockPosition, customBlock.getType());
|
breakBlock(player, blockPosition, customBlock.getType());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Chunk loadChunk(int chunkX, int chunkZ) {
|
public void loadChunk(int chunkX, int chunkZ, Consumer<Chunk> callback) {
|
||||||
Chunk chunk = getChunk(chunkX, chunkZ);
|
Chunk chunk = getChunk(chunkX, chunkZ);
|
||||||
return chunk == null ? createChunk(chunkX, chunkZ) : chunk; // TODO load from file
|
if (chunk != null) {
|
||||||
|
if (callback != null)
|
||||||
|
callback.accept(chunk);
|
||||||
|
} else {
|
||||||
|
retrieveChunk(chunkX, chunkZ, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadChunk(int chunkX, int chunkZ) {
|
||||||
|
loadChunk(chunkX, chunkZ, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void loadChunk(Position position, Consumer<Chunk> callback) {
|
||||||
|
int chunkX = Math.floorDiv((int) position.getX(), 16);
|
||||||
|
int chunkZ = Math.floorDiv((int) position.getY(), 16);
|
||||||
|
loadChunk(chunkX, chunkZ, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChunkLoaded(int chunkX, int chunkZ) {
|
||||||
|
return getChunk(chunkX, chunkZ) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public short getBlockId(int x, int y, int z) {
|
public short getBlockId(int x, int y, int z) {
|
||||||
@ -133,6 +158,23 @@ public class Instance implements BlockModifier {
|
|||||||
return getChunkAt(position.getX(), position.getZ());
|
return getChunkAt(position.getX(), position.getZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void saveToFolder(Runnable callback) {
|
||||||
|
if (folder == null)
|
||||||
|
throw new UnsupportedOperationException("You cannot save an instance without specified folder.");
|
||||||
|
|
||||||
|
Iterator<Chunk> chunks = getChunks().iterator();
|
||||||
|
while (chunks.hasNext()) {
|
||||||
|
Chunk chunk = chunks.next();
|
||||||
|
boolean isLast = !chunks.hasNext();
|
||||||
|
chunkLoaderIO.saveChunk(chunk, getFolder(), isLast ? callback : null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveToFolder() {
|
||||||
|
saveToFolder(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void setChunkGenerator(ChunkGenerator chunkGenerator) {
|
public void setChunkGenerator(ChunkGenerator chunkGenerator) {
|
||||||
this.chunkGenerator = chunkGenerator;
|
this.chunkGenerator = chunkGenerator;
|
||||||
}
|
}
|
||||||
@ -203,26 +245,50 @@ public class Instance implements BlockModifier {
|
|||||||
return uniqueId;
|
return uniqueId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendChunkUpdate(Player player, Chunk chunk) {
|
public File getFolder() {
|
||||||
ChunkDataPacket chunkDataPacket = new ChunkDataPacket();
|
return folder;
|
||||||
chunkDataPacket.fullChunk = false;
|
|
||||||
chunkDataPacket.chunk = chunk;
|
|
||||||
player.getPlayerConnection().sendPacket(chunkDataPacket); // TODO write packet buffer in another thread (Chunk packets are heavy)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Chunk createChunk(int chunkX, int chunkZ) {
|
public void setFolder(File folder) {
|
||||||
|
this.folder = folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendChunkUpdate(Player player, Chunk chunk) {
|
||||||
|
Buffer chunkData = chunk.getFullDataPacket();
|
||||||
|
chunkData.getData().retain(1).markReaderIndex();
|
||||||
|
player.getPlayerConnection().sendUnencodedPacket(chunkData);
|
||||||
|
chunkData.getData().resetReaderIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void retrieveChunk(int chunkX, int chunkZ, Consumer<Chunk> callback) {
|
||||||
|
if (folder != null) {
|
||||||
|
// Load from file if possible
|
||||||
|
chunkLoaderIO.loadChunk(chunkX, chunkZ, this, chunk -> {
|
||||||
|
cacheChunk(chunk);
|
||||||
|
if (callback != null)
|
||||||
|
callback.accept(chunk);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
createChunk(chunkX, chunkZ, callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createChunk(int chunkX, int chunkZ, Consumer<Chunk> callback) {
|
||||||
Biome biome = chunkGenerator != null ? chunkGenerator.getBiome(chunkX, chunkZ) : Biome.VOID;
|
Biome biome = chunkGenerator != null ? chunkGenerator.getBiome(chunkX, chunkZ) : Biome.VOID;
|
||||||
Chunk chunk = new Chunk(biome, chunkX, chunkZ);
|
Chunk chunk = new Chunk(biome, chunkX, chunkZ);
|
||||||
this.objectEntities.addCollection(chunk.objectEntities);
|
cacheChunk(chunk);
|
||||||
this.creatures.addCollection(chunk.creatures);
|
|
||||||
this.players.addCollection(chunk.players);
|
|
||||||
this.chunks.put(getChunkKey(chunkX, chunkZ), chunk);
|
|
||||||
if (chunkGenerator != null) {
|
if (chunkGenerator != null) {
|
||||||
ChunkBatch chunkBatch = createChunkBatch(chunk);
|
ChunkBatch chunkBatch = createChunkBatch(chunk);
|
||||||
chunkGenerator.generateChunkData(chunkBatch, chunkX, chunkZ);
|
chunkGenerator.generateChunkData(chunkBatch, chunkX, chunkZ);
|
||||||
chunkBatch.flush();
|
chunkBatch.flush(callback);
|
||||||
}
|
}
|
||||||
return chunk;
|
}
|
||||||
|
|
||||||
|
private void cacheChunk(Chunk chunk) {
|
||||||
|
this.objectEntities.addCollection(chunk.objectEntities);
|
||||||
|
this.creatures.addCollection(chunk.creatures);
|
||||||
|
this.players.addCollection(chunk.players);
|
||||||
|
this.chunks.put(getChunkKey(chunk.getChunkX(), chunk.getChunkZ()), chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ChunkBatch createChunkBatch(Chunk chunk) {
|
protected ChunkBatch createChunkBatch(Chunk chunk) {
|
||||||
@ -230,6 +296,9 @@ public class Instance implements BlockModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void sendChunkUpdate(Chunk chunk) {
|
protected void sendChunkUpdate(Chunk chunk) {
|
||||||
|
if (getPlayers().isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
Buffer chunkData = chunk.getFullDataPacket();
|
Buffer chunkData = chunk.getFullDataPacket();
|
||||||
chunkData.getData().retain(getPlayers().size()).markReaderIndex();
|
chunkData.getData().retain(getPlayers().size()).markReaderIndex();
|
||||||
getPlayers().forEach(player -> {
|
getPlayers().forEach(player -> {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package fr.themode.minestom.instance;
|
package fr.themode.minestom.instance;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@ -9,12 +10,16 @@ public class InstanceManager {
|
|||||||
|
|
||||||
private Set<Instance> instances = Collections.synchronizedSet(new HashSet<>());
|
private Set<Instance> instances = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
|
||||||
public Instance createInstance() {
|
public Instance createInstance(File folder) {
|
||||||
Instance instance = new Instance(UUID.randomUUID());
|
Instance instance = new Instance(UUID.randomUUID(), folder);
|
||||||
this.instances.add(instance);
|
this.instances.add(instance);
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Instance createInstance() {
|
||||||
|
return createInstance(null);
|
||||||
|
}
|
||||||
|
|
||||||
public Set<Instance> getInstances() {
|
public Set<Instance> getInstances() {
|
||||||
return Collections.unmodifiableSet(instances);
|
return Collections.unmodifiableSet(instances);
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
package fr.themode.minestom.listener;
|
package fr.themode.minestom.listener;
|
||||||
|
|
||||||
import fr.themode.minestom.entity.Player;
|
import fr.themode.minestom.entity.Player;
|
||||||
|
import fr.themode.minestom.event.AnimationEvent;
|
||||||
import fr.themode.minestom.net.packet.client.play.ClientAnimationPacket;
|
import fr.themode.minestom.net.packet.client.play.ClientAnimationPacket;
|
||||||
import fr.themode.minestom.net.packet.server.play.AnimationPacket;
|
import fr.themode.minestom.net.packet.server.play.AnimationPacket;
|
||||||
|
|
||||||
public class AnimationListener {
|
public class AnimationListener {
|
||||||
|
|
||||||
public static void animationListener(ClientAnimationPacket packet, Player player) {
|
public static void animationListener(ClientAnimationPacket packet, Player player) {
|
||||||
|
AnimationEvent animationEvent = new AnimationEvent(packet.hand);
|
||||||
|
player.callCancellableEvent(AnimationEvent.class, animationEvent, () -> {
|
||||||
AnimationPacket animationPacket = new AnimationPacket();
|
AnimationPacket animationPacket = new AnimationPacket();
|
||||||
animationPacket.entityId = player.getEntityId();
|
animationPacket.entityId = player.getEntityId();
|
||||||
animationPacket.animation = packet.hand == Player.Hand.MAIN ? AnimationPacket.Animation.SWING_MAIN_ARM : AnimationPacket.Animation.SWING_OFF_HAND;
|
animationPacket.animation = animationEvent.getHand() == Player.Hand.MAIN ? AnimationPacket.Animation.SWING_MAIN_ARM : AnimationPacket.Animation.SWING_OFF_HAND;
|
||||||
player.sendPacketToViewers(animationPacket);
|
player.sendPacketToViewers(animationPacket);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ public class BlockPlacementListener {
|
|||||||
int offsetZ = blockFace == ClientPlayerDiggingPacket.BlockFace.NORTH ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.SOUTH ? 1 : 0;
|
int offsetZ = blockFace == ClientPlayerDiggingPacket.BlockFace.NORTH ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.SOUTH ? 1 : 0;
|
||||||
|
|
||||||
blockPosition.add(offsetX, offsetY, offsetZ);
|
blockPosition.add(offsetX, offsetY, offsetZ);
|
||||||
BlockPlaceEvent blockPlaceEvent = new BlockPlaceEvent((short) 10, blockPosition);
|
BlockPlaceEvent blockPlaceEvent = new BlockPlaceEvent((short) 10, blockPosition, packet.hand);
|
||||||
player.callEvent(BlockPlaceEvent.class, blockPlaceEvent);
|
player.callEvent(BlockPlaceEvent.class, blockPlaceEvent);
|
||||||
if (!blockPlaceEvent.isCancelled()) {
|
if (!blockPlaceEvent.isCancelled()) {
|
||||||
instance.setBlock(blockPosition, "custom_block");
|
instance.setBlock(blockPosition, "custom_block");
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package fr.themode.minestom.listener;
|
package fr.themode.minestom.listener;
|
||||||
|
|
||||||
import fr.themode.minestom.entity.Entity;
|
import fr.themode.minestom.entity.Entity;
|
||||||
|
import fr.themode.minestom.entity.EntityCreature;
|
||||||
import fr.themode.minestom.entity.Player;
|
import fr.themode.minestom.entity.Player;
|
||||||
import fr.themode.minestom.event.AttackEvent;
|
import fr.themode.minestom.event.AttackEvent;
|
||||||
import fr.themode.minestom.event.InteractEvent;
|
import fr.themode.minestom.event.InteractEvent;
|
||||||
@ -14,6 +15,9 @@ public class UseEntityListener {
|
|||||||
return;
|
return;
|
||||||
ClientUseEntityPacket.Type type = packet.type;
|
ClientUseEntityPacket.Type type = packet.type;
|
||||||
if (type == ClientUseEntityPacket.Type.ATTACK) {
|
if (type == ClientUseEntityPacket.Type.ATTACK) {
|
||||||
|
if (entity instanceof EntityCreature && ((EntityCreature) entity).isDead()) // Can't attack dead entities
|
||||||
|
return;
|
||||||
|
|
||||||
AttackEvent attackEvent = new AttackEvent(entity);
|
AttackEvent attackEvent = new AttackEvent(entity);
|
||||||
player.callEvent(AttackEvent.class, attackEvent);
|
player.callEvent(AttackEvent.class, attackEvent);
|
||||||
} else if (type == ClientUseEntityPacket.Type.INTERACT) {
|
} else if (type == ClientUseEntityPacket.Type.INTERACT) {
|
||||||
|
@ -50,7 +50,7 @@ public class PacketProcessor {
|
|||||||
ConnectionState connectionState = playerConnection.getConnectionState();
|
ConnectionState connectionState = playerConnection.getConnectionState();
|
||||||
|
|
||||||
if (!printBlackList.contains(id)) {
|
if (!printBlackList.contains(id)) {
|
||||||
System.out.println("RECEIVED ID: 0x" + Integer.toHexString(id) + " State: " + connectionState);
|
//System.out.println("RECEIVED ID: 0x" + Integer.toHexString(id) + " State: " + connectionState);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connectionState == ConnectionState.UNKNOWN) {
|
if (connectionState == ConnectionState.UNKNOWN) {
|
||||||
|
@ -17,7 +17,7 @@ public class PacketWriter {
|
|||||||
public static void writeCallbackPacket(ServerPacket serverPacket, Consumer<Buffer> consumer) {
|
public static void writeCallbackPacket(ServerPacket serverPacket, Consumer<Buffer> consumer) {
|
||||||
batchesPool.submit(() -> {
|
batchesPool.submit(() -> {
|
||||||
Packet p = PacketUtils.writePacket(serverPacket);
|
Packet p = PacketUtils.writePacket(serverPacket);
|
||||||
consumer.accept(PacketUtils.encode(p));
|
consumer.accept(PacketUtils.encode(p)); // TODO accept in another thread?
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,25 +2,13 @@ package fr.themode.minestom.net.packet.client.login;
|
|||||||
|
|
||||||
import fr.adamaq01.ozao.net.Buffer;
|
import fr.adamaq01.ozao.net.Buffer;
|
||||||
import fr.themode.minestom.Main;
|
import fr.themode.minestom.Main;
|
||||||
import fr.themode.minestom.bossbar.BarColor;
|
|
||||||
import fr.themode.minestom.bossbar.BarDivision;
|
|
||||||
import fr.themode.minestom.bossbar.BossBar;
|
|
||||||
import fr.themode.minestom.entity.GameMode;
|
import fr.themode.minestom.entity.GameMode;
|
||||||
import fr.themode.minestom.entity.ItemEntity;
|
|
||||||
import fr.themode.minestom.entity.Player;
|
import fr.themode.minestom.entity.Player;
|
||||||
import fr.themode.minestom.entity.demo.ChickenCreature;
|
|
||||||
import fr.themode.minestom.entity.demo.TestArrow;
|
|
||||||
import fr.themode.minestom.instance.Instance;
|
|
||||||
import fr.themode.minestom.instance.demo.ChunkGeneratorDemo;
|
|
||||||
import fr.themode.minestom.inventory.PlayerInventory;
|
|
||||||
import fr.themode.minestom.item.ItemStack;
|
|
||||||
import fr.themode.minestom.item.Material;
|
|
||||||
import fr.themode.minestom.net.ConnectionManager;
|
import fr.themode.minestom.net.ConnectionManager;
|
||||||
import fr.themode.minestom.net.ConnectionState;
|
import fr.themode.minestom.net.ConnectionState;
|
||||||
import fr.themode.minestom.net.packet.client.ClientPreplayPacket;
|
import fr.themode.minestom.net.packet.client.ClientPreplayPacket;
|
||||||
import fr.themode.minestom.net.packet.server.login.JoinGamePacket;
|
import fr.themode.minestom.net.packet.server.login.JoinGamePacket;
|
||||||
import fr.themode.minestom.net.packet.server.login.LoginSuccessPacket;
|
import fr.themode.minestom.net.packet.server.login.LoginSuccessPacket;
|
||||||
import fr.themode.minestom.net.packet.server.play.DeclareCommandsPacket;
|
|
||||||
import fr.themode.minestom.net.packet.server.play.PlayerInfoPacket;
|
import fr.themode.minestom.net.packet.server.play.PlayerInfoPacket;
|
||||||
import fr.themode.minestom.net.packet.server.play.PlayerPositionAndLookPacket;
|
import fr.themode.minestom.net.packet.server.play.PlayerPositionAndLookPacket;
|
||||||
import fr.themode.minestom.net.packet.server.play.SpawnPositionPacket;
|
import fr.themode.minestom.net.packet.server.play.SpawnPositionPacket;
|
||||||
@ -34,21 +22,24 @@ import java.util.UUID;
|
|||||||
public class LoginStartPacket implements ClientPreplayPacket {
|
public class LoginStartPacket implements ClientPreplayPacket {
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
private static Instance instance;
|
/*private static Instance instance;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
ChunkGeneratorDemo chunkGeneratorDemo = new ChunkGeneratorDemo();
|
ChunkGeneratorDemo chunkGeneratorDemo = new ChunkGeneratorDemo();
|
||||||
instance = Main.getInstanceManager().createInstance();
|
instance = Main.getInstanceManager().createInstance(new File("C:\\Users\\themo\\OneDrive\\Bureau\\Minestom data"));
|
||||||
|
//instance = Main.getInstanceManager().createInstance();
|
||||||
instance.setChunkGenerator(chunkGeneratorDemo);
|
instance.setChunkGenerator(chunkGeneratorDemo);
|
||||||
int loopStart = -2;
|
int loopStart = -2;
|
||||||
int loopEnd = 2;
|
int loopEnd = 2;
|
||||||
long time = System.currentTimeMillis();
|
long time = System.currentTimeMillis();
|
||||||
for (int x = loopStart; x < loopEnd; x++)
|
for (int x = loopStart; x < loopEnd; x++)
|
||||||
for (int z = loopStart; z < loopEnd; z++) {
|
for (int z = loopStart; z < loopEnd; z++) {
|
||||||
instance.loadChunk(x, z);
|
instance.loadChunk(x, z, chunk -> {
|
||||||
|
System.out.println("JE SUIS LE CALLBACK CHUNK");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
System.out.println("Time to load all chunks: " + (System.currentTimeMillis() - time) + " ms");
|
System.out.println("Time to load all chunks: " + (System.currentTimeMillis() - time) + " ms");
|
||||||
}
|
}*/
|
||||||
|
|
||||||
public String username;
|
public String username;
|
||||||
|
|
||||||
@ -73,9 +64,9 @@ public class LoginStartPacket implements ClientPreplayPacket {
|
|||||||
GameMode gameMode = GameMode.SURVIVAL;
|
GameMode gameMode = GameMode.SURVIVAL;
|
||||||
Dimension dimension = Dimension.OVERWORLD;
|
Dimension dimension = Dimension.OVERWORLD;
|
||||||
LevelType levelType = LevelType.DEFAULT;
|
LevelType levelType = LevelType.DEFAULT;
|
||||||
float x = 5;
|
float x = 0;
|
||||||
float y = 65;
|
float y = 0;
|
||||||
float z = 5;
|
float z = 0;
|
||||||
|
|
||||||
player.refreshDimension(dimension);
|
player.refreshDimension(dimension);
|
||||||
player.refreshGameMode(gameMode);
|
player.refreshGameMode(gameMode);
|
||||||
@ -121,10 +112,14 @@ public class LoginStartPacket implements ClientPreplayPacket {
|
|||||||
|
|
||||||
// Next is optional TODO put all that somewhere else (LoginEvent)
|
// Next is optional TODO put all that somewhere else (LoginEvent)
|
||||||
// TODO LoginEvent in another thread (here we are in netty thread)
|
// TODO LoginEvent in another thread (here we are in netty thread)
|
||||||
|
System.out.println("ADD WAITING PLAYER");
|
||||||
|
Main.getEntityManager().addWaitingPlayer(player);
|
||||||
|
|
||||||
player.setInstance(instance);
|
|
||||||
|
|
||||||
for (int cx = 0; cx < 4; cx++)
|
// TODO REMOVE EVERYTHING DOWN THERE
|
||||||
|
//player.setInstance(instance);
|
||||||
|
|
||||||
|
/*for (int cx = 0; cx < 4; cx++)
|
||||||
for (int cz = 0; cz < 4; cz++) {
|
for (int cz = 0; cz < 4; cz++) {
|
||||||
ChickenCreature chickenCreature = new ChickenCreature();
|
ChickenCreature chickenCreature = new ChickenCreature();
|
||||||
chickenCreature.refreshPosition(0 + (float) cx * 1, 65, 0 + (float) cz * 1);
|
chickenCreature.refreshPosition(0 + (float) cx * 1, 65, 0 + (float) cz * 1);
|
||||||
@ -140,7 +135,7 @@ public class LoginStartPacket implements ClientPreplayPacket {
|
|||||||
/*Inventory inv = new Inventory(InventoryType.WINDOW_3X3, "Salut je suis le titre");
|
/*Inventory inv = new Inventory(InventoryType.WINDOW_3X3, "Salut je suis le titre");
|
||||||
inv.setItemStack(0, new ItemStack(1, (byte) 1));
|
inv.setItemStack(0, new ItemStack(1, (byte) 1));
|
||||||
player.openInventory(inv);
|
player.openInventory(inv);
|
||||||
inv.setItemStack(1, new ItemStack(1, (byte) 2));*/
|
inv.setItemStack(1, new ItemStack(1, (byte) 2));
|
||||||
|
|
||||||
BossBar bossBar = new BossBar("Bossbar Title", BarColor.BLUE, BarDivision.SEGMENT_12);
|
BossBar bossBar = new BossBar("Bossbar Title", BarColor.BLUE, BarDivision.SEGMENT_12);
|
||||||
bossBar.setProgress(0.75f);
|
bossBar.setProgress(0.75f);
|
||||||
@ -178,7 +173,7 @@ public class LoginStartPacket implements ClientPreplayPacket {
|
|||||||
declareCommandsPacket.rootIndex = 0;
|
declareCommandsPacket.rootIndex = 0;
|
||||||
|
|
||||||
|
|
||||||
connection.sendPacket(declareCommandsPacket);
|
connection.sendPacket(declareCommandsPacket);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
package fr.themode.minestom.net.packet.client.play;
|
package fr.themode.minestom.net.packet.client.play;
|
||||||
|
|
||||||
import fr.adamaq01.ozao.net.Buffer;
|
import fr.adamaq01.ozao.net.Buffer;
|
||||||
|
import fr.themode.minestom.entity.Player;
|
||||||
import fr.themode.minestom.net.packet.client.ClientPlayPacket;
|
import fr.themode.minestom.net.packet.client.ClientPlayPacket;
|
||||||
import fr.themode.minestom.utils.BlockPosition;
|
import fr.themode.minestom.utils.BlockPosition;
|
||||||
import fr.themode.minestom.utils.Utils;
|
import fr.themode.minestom.utils.Utils;
|
||||||
|
|
||||||
public class ClientPlayerBlockPlacementPacket extends ClientPlayPacket {
|
public class ClientPlayerBlockPlacementPacket extends ClientPlayPacket {
|
||||||
|
|
||||||
public Hand hand;
|
public Player.Hand hand;
|
||||||
public BlockPosition blockPosition;
|
public BlockPosition blockPosition;
|
||||||
public ClientPlayerDiggingPacket.BlockFace blockFace;
|
public ClientPlayerDiggingPacket.BlockFace blockFace;
|
||||||
public float cursorPositionX, cursorPositionY, cursorPositionZ;
|
public float cursorPositionX, cursorPositionY, cursorPositionZ;
|
||||||
@ -15,7 +16,7 @@ public class ClientPlayerBlockPlacementPacket extends ClientPlayPacket {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(Buffer buffer) {
|
public void read(Buffer buffer) {
|
||||||
this.hand = Hand.values()[Utils.readVarInt(buffer)];
|
this.hand = Player.Hand.values()[Utils.readVarInt(buffer)];
|
||||||
this.blockPosition = Utils.readPosition(buffer);
|
this.blockPosition = Utils.readPosition(buffer);
|
||||||
this.blockFace = ClientPlayerDiggingPacket.BlockFace.values()[Utils.readVarInt(buffer)];
|
this.blockFace = ClientPlayerDiggingPacket.BlockFace.values()[Utils.readVarInt(buffer)];
|
||||||
this.cursorPositionX = buffer.getFloat();
|
this.cursorPositionX = buffer.getFloat();
|
||||||
@ -24,9 +25,4 @@ public class ClientPlayerBlockPlacementPacket extends ClientPlayPacket {
|
|||||||
this.insideBlock = buffer.getBoolean();
|
this.insideBlock = buffer.getBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Hand {
|
|
||||||
MAIN_HAND,
|
|
||||||
OFF_HAND;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import fr.adamaq01.ozao.net.Buffer;
|
|||||||
import fr.themode.minestom.instance.Chunk;
|
import fr.themode.minestom.instance.Chunk;
|
||||||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||||
import fr.themode.minestom.utils.BlockPosition;
|
import fr.themode.minestom.utils.BlockPosition;
|
||||||
|
import fr.themode.minestom.utils.SerializerUtils;
|
||||||
import fr.themode.minestom.utils.Utils;
|
import fr.themode.minestom.utils.Utils;
|
||||||
import net.querz.nbt.CompoundTag;
|
import net.querz.nbt.CompoundTag;
|
||||||
import net.querz.nbt.DoubleTag;
|
import net.querz.nbt.DoubleTag;
|
||||||
@ -81,7 +82,7 @@ public class ChunkDataPacket implements ServerPacket {
|
|||||||
Utils.writeVarInt(buffer, blockEntities.size());
|
Utils.writeVarInt(buffer, blockEntities.size());
|
||||||
|
|
||||||
for (Integer index : blockEntities) {
|
for (Integer index : blockEntities) {
|
||||||
BlockPosition blockPosition = indexToBlockPosition(index);
|
BlockPosition blockPosition = SerializerUtils.indexToBlockPosition(index, chunk.getChunkX(), chunk.getChunkZ());
|
||||||
CompoundTag blockEntity = new CompoundTag();
|
CompoundTag blockEntity = new CompoundTag();
|
||||||
blockEntity.put("x", new DoubleTag(blockPosition.getX()));
|
blockEntity.put("x", new DoubleTag(blockPosition.getX()));
|
||||||
blockEntity.put("y", new DoubleTag(blockPosition.getY()));
|
blockEntity.put("y", new DoubleTag(blockPosition.getY()));
|
||||||
@ -110,13 +111,6 @@ public class ChunkDataPacket implements ServerPacket {
|
|||||||
return blocks;
|
return blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlockPosition indexToBlockPosition(int index) {
|
|
||||||
byte z = (byte) (index >> 12 & 0xF);
|
|
||||||
byte y = (byte) (index >> 4 & 0xFF);
|
|
||||||
byte x = (byte) (index >> 0 & 0xF);
|
|
||||||
return new BlockPosition(x + 16 * chunk.getChunkX(), y, z + 16 * chunk.getChunkZ());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return 0x21;
|
return 0x21;
|
||||||
|
@ -6,6 +6,7 @@ import fr.adamaq01.ozao.net.server.Connection;
|
|||||||
import fr.themode.minestom.net.ConnectionState;
|
import fr.themode.minestom.net.ConnectionState;
|
||||||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||||
import fr.themode.minestom.utils.PacketUtils;
|
import fr.themode.minestom.utils.PacketUtils;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.socket.SocketChannel;
|
import io.netty.channel.socket.SocketChannel;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@ -40,12 +41,15 @@ public class PlayerConnection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void sendUnencodedPacket(Buffer packet) {
|
public void sendUnencodedPacket(Buffer packet) {
|
||||||
try {
|
getChannel().writeAndFlush(packet.getData());
|
||||||
SocketChannel channel = ((SocketChannel) field.get(connection));
|
|
||||||
channel.writeAndFlush(packet.getData());
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void writeUnencodedPacket(Buffer packet) {
|
||||||
|
getChannel().write(packet.getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flush() {
|
||||||
|
getChannel().flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sendPacket(ServerPacket serverPacket) {
|
public void sendPacket(ServerPacket serverPacket) {
|
||||||
@ -63,4 +67,13 @@ public class PlayerConnection {
|
|||||||
public ConnectionState getConnectionState() {
|
public ConnectionState getConnectionState() {
|
||||||
return connectionState;
|
return connectionState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Channel getChannel() {
|
||||||
|
try {
|
||||||
|
return ((SocketChannel) field.get(connection));
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
20
src/main/java/fr/themode/minestom/utils/ArraysUtils.java
Normal file
20
src/main/java/fr/themode/minestom/utils/ArraysUtils.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package fr.themode.minestom.utils;
|
||||||
|
|
||||||
|
public class ArraysUtils {
|
||||||
|
|
||||||
|
public static byte[] concenateByteArrays(byte[]... arrays) {
|
||||||
|
int totalLength = 0;
|
||||||
|
for (byte[] array : arrays) {
|
||||||
|
totalLength += array.length;
|
||||||
|
}
|
||||||
|
byte[] result = new byte[totalLength];
|
||||||
|
|
||||||
|
int startingPos = 0;
|
||||||
|
for (byte[] array : arrays) {
|
||||||
|
System.arraycopy(array, 0, result, startingPos, array.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
42
src/main/java/fr/themode/minestom/utils/SerializerUtils.java
Normal file
42
src/main/java/fr/themode/minestom/utils/SerializerUtils.java
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package fr.themode.minestom.utils;
|
||||||
|
|
||||||
|
public class SerializerUtils {
|
||||||
|
|
||||||
|
public static byte[] intToBytes(int value) {
|
||||||
|
byte[] result = new byte[4];
|
||||||
|
result[0] = (byte) (value >> 24);
|
||||||
|
result[1] = (byte) (value >> 16);
|
||||||
|
result[2] = (byte) (value >> 8);
|
||||||
|
result[3] = (byte) (value >> 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int bytesToInt(byte[] value) {
|
||||||
|
return ((value[0] & 0xFF) << 24) |
|
||||||
|
((value[1] & 0xFF) << 16) |
|
||||||
|
((value[2] & 0xFF) << 8) |
|
||||||
|
((value[3] & 0xFF) << 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int chunkCoordToIndex(byte x, byte y, byte z) {
|
||||||
|
short index = (short) (x & 0x000F);
|
||||||
|
index |= (y << 4) & 0x0FF0;
|
||||||
|
index |= (z << 12) & 0xF000;
|
||||||
|
return index & 0xffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] indexToChunkPosition(int index) {
|
||||||
|
byte z = (byte) (index >> 12 & 0xF);
|
||||||
|
byte y = (byte) (index >> 4 & 0xFF);
|
||||||
|
byte x = (byte) (index >> 0 & 0xF);
|
||||||
|
return new byte[]{x, y, z};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockPosition indexToBlockPosition(int index, int chunkX, int chunkZ) {
|
||||||
|
byte z = (byte) (index >> 12 & 0xF);
|
||||||
|
byte y = (byte) (index >> 4 & 0xFF);
|
||||||
|
byte x = (byte) (index >> 0 & 0xF);
|
||||||
|
return new BlockPosition(x + 16 * chunkX, y, z + 16 * chunkZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user