This commit is contained in:
TheMode 2019-09-01 06:18:41 +02:00
parent f43bf11e66
commit dedc17f42e
17 changed files with 198 additions and 79 deletions

View File

@ -20,4 +20,6 @@ dependencies {
implementation 'com.github.luben:zstd-jni:1.4.3-1' implementation 'com.github.luben:zstd-jni:1.4.3-1'
implementation 'com.esotericsoftware:reflectasm:1.11.9' implementation 'com.esotericsoftware:reflectasm:1.11.9'
implementation 'com.github.LynnOwens:starlite:9971b899f7' implementation 'com.github.LynnOwens:starlite:9971b899f7'
//implementation 'com.github.jhg023:SimpleNet:1.4.14'
} }

View File

@ -5,6 +5,7 @@ import fr.adamaq01.ozao.net.server.Connection;
import fr.adamaq01.ozao.net.server.Server; import fr.adamaq01.ozao.net.server.Server;
import fr.adamaq01.ozao.net.server.ServerHandler; import fr.adamaq01.ozao.net.server.ServerHandler;
import fr.adamaq01.ozao.net.server.backend.tcp.TCPServer; import fr.adamaq01.ozao.net.server.backend.tcp.TCPServer;
import fr.themode.minestom.data.DataManager;
import fr.themode.minestom.entity.EntityManager; import fr.themode.minestom.entity.EntityManager;
import fr.themode.minestom.entity.Player; import fr.themode.minestom.entity.Player;
import fr.themode.minestom.instance.BlockManager; import fr.themode.minestom.instance.BlockManager;
@ -42,6 +43,7 @@ public class Main {
private static InstanceManager instanceManager; private static InstanceManager instanceManager;
private static BlockManager blockManager; private static BlockManager blockManager;
private static EntityManager entityManager; private static EntityManager entityManager;
private static DataManager dataManager;
public static void main(String[] args) { public static void main(String[] args) {
connectionManager = new ConnectionManager(); connectionManager = new ConnectionManager();
@ -51,6 +53,7 @@ public class Main {
instanceManager = new InstanceManager(); instanceManager = new InstanceManager();
blockManager = new BlockManager(); blockManager = new BlockManager();
entityManager = new EntityManager(); entityManager = new EntityManager();
dataManager = new DataManager();
blockManager.registerBlock(StoneBlock::new); blockManager.registerBlock(StoneBlock::new);
@ -142,6 +145,10 @@ public class Main {
return entityManager; return entityManager;
} }
public static DataManager getDataManager() {
return dataManager;
}
public static ConnectionManager getConnectionManager() { public static ConnectionManager getConnectionManager() {
return connectionManager; return connectionManager;
} }

View File

@ -149,7 +149,7 @@ public abstract class Entity implements Viewable, DataContainer {
@Override @Override
public void addViewer(Player player) { public void addViewer(Player player) {
this.viewers.add(player); this.viewers.add(player);
player.viewableEntity.add(this); player.viewableEntities.add(this);
PlayerConnection playerConnection = player.getPlayerConnection(); PlayerConnection playerConnection = player.getPlayerConnection();
playerConnection.sendPacket(getVelocityPacket()); playerConnection.sendPacket(getVelocityPacket());
playerConnection.sendPacket(getPassengersPacket()); playerConnection.sendPacket(getPassengersPacket());
@ -165,7 +165,7 @@ public abstract class Entity implements Viewable, DataContainer {
destroyEntitiesPacket.entityIds = new int[]{getEntityId()}; destroyEntitiesPacket.entityIds = new int[]{getEntityId()};
player.getPlayerConnection().sendPacket(destroyEntitiesPacket); player.getPlayerConnection().sendPacket(destroyEntitiesPacket);
} }
player.viewableEntity.remove(this); player.viewableEntities.remove(this);
} }
@Override @Override

View File

@ -2,19 +2,14 @@ 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.PlayerLoginEvent;
import fr.themode.minestom.event.PlayerSpawnEvent;
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 fr.themode.minestom.utils.ChunkUtils;
import fr.themode.minestom.utils.Position;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue; 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;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
public class EntityManager { public class EntityManager {
@ -41,35 +36,8 @@ public class EntityManager {
PlayerLoginEvent loginEvent = new PlayerLoginEvent(); PlayerLoginEvent loginEvent = new PlayerLoginEvent();
playerCache.callEvent(PlayerLoginEvent.class, loginEvent); playerCache.callEvent(PlayerLoginEvent.class, loginEvent);
Instance spawningInstance = loginEvent.getSpawningInstance() == null ? instanceManager.createInstanceContainer() : loginEvent.getSpawningInstance(); Instance spawningInstance = loginEvent.getSpawningInstance() == null ? instanceManager.createInstanceContainer() : loginEvent.getSpawningInstance();
Position position = playerCache.getPosition();
long[] visibleChunks = ChunkUtils.getChunksInRange(position, Main.CHUNK_VIEW_DISTANCE); playerCache.setInstance(spawningInstance);
int length = visibleChunks.length;
AtomicInteger counter = new AtomicInteger(0);
for (int i = 0; i < length; i++) {
int[] chunkPos = ChunkUtils.getChunkCoord(visibleChunks[i]);
int chunkX = chunkPos[0];
int chunkZ = chunkPos[1];
Consumer<Chunk> callback = (chunk) -> {
boolean isLast = counter.get() == length - 1;
if (isLast) {
// This is the last chunk to be loaded, spawn player
playerCache.spawned = true;
playerCache.setInstance(spawningInstance);
PlayerSpawnEvent spawnEvent = new PlayerSpawnEvent();
playerCache.callEvent(PlayerSpawnEvent.class, spawnEvent);
playerCache.updateViewPosition(chunk);
} else {
// Increment the counter of current loaded chunks
counter.incrementAndGet();
}
};
// WARNING: if auto load is disabled and no chunks are loaded beforehand, player will be stuck.
spawningInstance.loadChunk(chunkX, chunkZ, callback);
}
}); });
} }
@ -77,7 +45,6 @@ public class EntityManager {
// TODO optimize for when there are too many entities on one chunk // TODO optimize for when there are too many entities on one chunk
private void testTick2(Instance instance) { private void testTick2(Instance instance) {
for (Chunk chunk : instance.getChunks()) { for (Chunk chunk : instance.getChunks()) {
Set<Entity> entities = instance.getChunkEntities(chunk); Set<Entity> entities = instance.getChunkEntities(chunk);

View File

@ -29,6 +29,8 @@ import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
public class Player extends LivingEntity { public class Player extends LivingEntity {
@ -62,7 +64,8 @@ public class Player extends LivingEntity {
System.out.println("Time to load all chunks: " + (System.currentTimeMillis() - time) + " ms"); System.out.println("Time to load all chunks: " + (System.currentTimeMillis() - time) + " ms");
} }
protected Set<Entity> viewableEntity = new CopyOnWriteArraySet<>(); protected Set<Entity> viewableEntities = new CopyOnWriteArraySet<>();
protected Set<Chunk> viewableChunks = new CopyOnWriteArraySet<>();
private PlayerSettings settings; private PlayerSettings settings;
private float exp; private float exp;
@ -87,8 +90,6 @@ public class Player extends LivingEntity {
private boolean jump; private boolean jump;
private boolean unmount; private boolean unmount;
protected boolean spawned;
public Player(UUID uuid, String username, PlayerConnection playerConnection) { public Player(UUID uuid, String username, PlayerConnection playerConnection) {
super(100); super(100);
this.uuid = uuid; this.uuid = uuid;
@ -247,6 +248,9 @@ public class Player extends LivingEntity {
} }
} }
// Tick event
callEvent(PlayerTickEvent.class, new PlayerTickEvent());
// Multiplayer sync // Multiplayer sync
Position position = getPosition(); Position position = getPosition();
@ -324,7 +328,7 @@ public class Player extends LivingEntity {
clearBossBars(); clearBossBars();
if (getOpenInventory() != null) if (getOpenInventory() != null)
getOpenInventory().removeViewer(this); getOpenInventory().removeViewer(this);
this.viewableEntity.forEach(entity -> entity.removeViewer(this)); this.viewableEntities.forEach(entity -> entity.removeViewer(this));
super.remove(); super.remove();
} }
@ -367,10 +371,46 @@ public class Player extends LivingEntity {
@Override @Override
public void setInstance(Instance instance) { public void setInstance(Instance instance) {
if (!spawned) if (instance == null)
throw new IllegalStateException("Player#setInstance is only available during and after PlayerSpawnEvent"); throw new IllegalArgumentException("instance cannot be null!");
if (this.instance == instance)
throw new IllegalArgumentException("Instance should be different than the current one");
super.setInstance(instance); for (Chunk viewableChunk : viewableChunks) {
viewableChunk.removeViewer(this);
}
viewableChunks.clear();
long[] visibleChunks = ChunkUtils.getChunksInRange(position, getChunkRange());
int length = visibleChunks.length;
AtomicInteger counter = new AtomicInteger(0);
for (int i = 0; i < length; i++) {
int[] chunkPos = ChunkUtils.getChunkCoord(visibleChunks[i]);
int chunkX = chunkPos[0];
int chunkZ = chunkPos[1];
Consumer<Chunk> callback = (chunk) -> {
if (chunk != null) {
viewableChunks.add(chunk);
chunk.addViewer(this);
}
boolean isLast = counter.get() == length - 1;
if (isLast) {
// This is the last chunk to be loaded, spawn player
super.setInstance(instance);
PlayerSpawnEvent spawnEvent = new PlayerSpawnEvent(instance);
callEvent(PlayerSpawnEvent.class, spawnEvent);
updateViewPosition(chunk);
} else {
// Increment the counter of current loaded chunks
counter.incrementAndGet();
}
};
// WARNING: if auto load is disabled and no chunks are loaded beforehand, player will be stuck.
instance.loadOptionalChunk(chunkX, chunkZ, callback);
}
} }
@Override @Override
@ -516,6 +556,10 @@ public class Player extends LivingEntity {
unloadChunkPacket.chunkX = chunkPos[0]; unloadChunkPacket.chunkX = chunkPos[0];
unloadChunkPacket.chunkZ = chunkPos[1]; unloadChunkPacket.chunkZ = chunkPos[1];
playerConnection.sendPacket(unloadChunkPacket); playerConnection.sendPacket(unloadChunkPacket);
Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]);
if (chunk != null)
chunk.removeViewer(this);
} }
updateViewPosition(newChunk); updateViewPosition(newChunk);
@ -529,6 +573,8 @@ public class Player extends LivingEntity {
if (chunk == null) { if (chunk == null) {
return; // Cannot load chunk (auto load is not enabled) return; // Cannot load chunk (auto load is not enabled)
} }
this.viewableChunks.add(chunk);
chunk.addViewer(this);
instance.sendChunk(this, chunk); instance.sendChunk(this, chunk);
if (isFar && isLast) { if (isFar && isLast) {
updatePlayerPosition(); updatePlayerPosition();
@ -678,6 +724,10 @@ public class Player extends LivingEntity {
inventory.update(); inventory.update();
} }
public Set<Chunk> getViewableChunks() {
return Collections.unmodifiableSet(viewableChunks);
}
public void clearBossBars() { public void clearBossBars() {
this.bossBars.forEach(bossBar -> bossBar.removeViewer(this)); this.bossBars.forEach(bossBar -> bossBar.removeViewer(this));
} }
@ -767,6 +817,12 @@ public class Player extends LivingEntity {
this.unmount = unmount; this.unmount = unmount;
} }
public int getChunkRange() {
int serverRange = Main.CHUNK_VIEW_DISTANCE;
int playerRange = getSettings().viewDistance;
return serverRange;//playerRange < serverRange ? playerRange : serverRange;
}
public long getLastKeepAlive() { public long getLastKeepAlive() {
return lastKeepAlive; return lastKeepAlive;
} }

View File

@ -1,5 +1,16 @@
package fr.themode.minestom.event; package fr.themode.minestom.event;
import fr.themode.minestom.instance.Instance;
public class PlayerSpawnEvent extends Event { public class PlayerSpawnEvent extends Event {
private Instance spawnInstance;
public PlayerSpawnEvent(Instance spawnInstance) {
this.spawnInstance = spawnInstance;
}
public Instance getSpawnInstance() {
return spawnInstance;
}
} }

View File

@ -0,0 +1,4 @@
package fr.themode.minestom.event;
public class PlayerTickEvent extends Event {
}

View File

@ -3,6 +3,8 @@ package fr.themode.minestom.instance;
import fr.adamaq01.ozao.net.Buffer; import fr.adamaq01.ozao.net.Buffer;
import fr.adamaq01.ozao.net.packet.Packet; import fr.adamaq01.ozao.net.packet.Packet;
import fr.themode.minestom.Main; import fr.themode.minestom.Main;
import fr.themode.minestom.Viewable;
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 fr.themode.minestom.utils.SerializerUtils;
@ -10,10 +12,11 @@ import fr.themode.minestom.utils.SerializerUtils;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
import java.io.IOException; import java.io.IOException;
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 implements Viewable {
public static final int CHUNK_SIZE_X = 16; public static final int CHUNK_SIZE_X = 16;
public static final int CHUNK_SIZE_Y = 256; public static final int CHUNK_SIZE_Y = 256;
@ -29,6 +32,7 @@ public class Chunk {
private Set<Integer> blockEntities = new CopyOnWriteArraySet<>(); private Set<Integer> blockEntities = new CopyOnWriteArraySet<>();
// Cache // Cache
private Set<Player> viewers = new CopyOnWriteArraySet<>();
private Buffer fullDataPacket; private Buffer fullDataPacket;
public Chunk(Biome biome, int chunkX, int chunkZ) { public Chunk(Biome biome, int chunkX, int chunkZ) {
@ -155,4 +159,21 @@ public class Chunk {
public String toString() { public String toString() {
return "Chunk[" + chunkX + ":" + chunkZ + "]"; return "Chunk[" + chunkX + ":" + chunkZ + "]";
} }
// UNSAFE
@Override
public void addViewer(Player player) {
this.viewers.add(player);
}
// UNSAFE
@Override
public void removeViewer(Player player) {
this.viewers.remove(player);
}
@Override
public Set<Player> getViewers() {
return Collections.unmodifiableSet(viewers);
}
} }

View File

@ -54,6 +54,8 @@ public abstract class Instance implements BlockModifier {
public abstract void sendChunkUpdate(Player player, Chunk chunk); public abstract void sendChunkUpdate(Player player, Chunk chunk);
public abstract void sendChunkSectionUpdate(Chunk chunk, int section, Player player);
protected abstract void retrieveChunk(int chunkX, int chunkZ, Consumer<Chunk> callback); protected abstract void retrieveChunk(int chunkX, int chunkZ, Consumer<Chunk> callback);
public abstract void createChunk(int chunkX, int chunkZ, Consumer<Chunk> callback); public abstract void createChunk(int chunkX, int chunkZ, Consumer<Chunk> callback);

View File

@ -4,6 +4,8 @@ import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.entity.Player; import fr.themode.minestom.entity.Player;
import fr.themode.minestom.event.PlayerBlockBreakEvent; import fr.themode.minestom.event.PlayerBlockBreakEvent;
import fr.themode.minestom.net.PacketWriterUtils; import fr.themode.minestom.net.PacketWriterUtils;
import fr.themode.minestom.net.packet.server.play.BlockChangePacket;
import fr.themode.minestom.net.packet.server.play.ChunkDataPacket;
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.ChunkUtils; import fr.themode.minestom.utils.ChunkUtils;
@ -37,11 +39,11 @@ public class InstanceContainer extends Instance {
public synchronized void setBlock(int x, int y, int z, short blockId) { public synchronized void setBlock(int x, int y, int z, short blockId) {
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); byte chunkX = (byte) (x % 16);
PacketWriterUtils.writeCallbackPacket(chunk.getFreshPartialDataPacket(), buffer -> { byte chunkY = (byte) y;
chunk.setFullDataPacket(buffer); byte chunkZ = (byte) (z % 16);
sendChunkUpdate(chunk); chunk.setBlock(chunkX, chunkY, chunkZ, blockId);
}); sendBlockChange(chunk, x, y, z, blockId);
} }
} }
@ -49,21 +51,21 @@ public class InstanceContainer extends Instance {
public synchronized void setBlock(int x, int y, int z, String blockId) { public synchronized void setBlock(int x, int y, int z, String blockId) {
Chunk chunk = getChunkAt(x, z); Chunk chunk = getChunkAt(x, z);
synchronized (chunk) { synchronized (chunk) {
chunk.setCustomBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId); byte chunkX = (byte) (x % 16);
PacketWriterUtils.writeCallbackPacket(chunk.getFreshPartialDataPacket(), buffer -> { byte chunkY = (byte) y;
chunk.setFullDataPacket(buffer); byte chunkZ = (byte) (z % 16);
sendChunkUpdate(chunk); chunk.setCustomBlock(chunkX, chunkY, chunkZ, blockId);
}); short id = chunk.getBlockId(chunkX, chunkY, chunkZ);
sendBlockChange(chunk, x, y, z, id);
} }
} }
// TODO deplace
@Override @Override
public void breakBlock(Player player, BlockPosition blockPosition) { public void breakBlock(Player player, BlockPosition blockPosition) {
Chunk chunk = getChunkAt(blockPosition); Chunk chunk = getChunkAt(blockPosition);
short blockId = chunk.getBlockId((byte) (blockPosition.getX() % 16), (byte) blockPosition.getY(), (byte) (blockPosition.getZ() % 16)); short blockId = chunk.getBlockId((byte) (blockPosition.getX() % 16), (byte) blockPosition.getY(), (byte) (blockPosition.getZ() % 16));
if (blockId == 0) { if (blockId == 0) {
sendChunkUpdate(player, chunk); sendChunkSectionUpdate(chunk, ChunkUtils.getSectionAt(blockPosition.getY()), player);
return; return;
} }
@ -87,10 +89,9 @@ public class InstanceContainer extends Instance {
particlePacket.particleData = 0.3f; particlePacket.particleData = 0.3f;
particlePacket.particleCount = 100; particlePacket.particleCount = 100;
particlePacket.blockId = blockId; particlePacket.blockId = blockId;
player.getPlayerConnection().sendPacket(particlePacket); chunk.sendPacketToViewers(particlePacket);
player.sendPacketToViewers(particlePacket);
} else { } else {
sendChunkUpdate(player, chunk); sendChunkSectionUpdate(chunk, ChunkUtils.getSectionAt(blockPosition.getY()), player);
} }
} }
@ -156,6 +157,17 @@ public class InstanceContainer extends Instance {
chunkData.getData().resetReaderIndex(); chunkData.getData().resetReaderIndex();
} }
@Override
public void sendChunkSectionUpdate(Chunk chunk, int section, Player player) {
ChunkDataPacket chunkDataPacket = new ChunkDataPacket();
chunkDataPacket.fullChunk = false;
chunkDataPacket.chunk = chunk;
int[] sections = new int[16];
sections[section] = 1;
chunkDataPacket.sections = sections;
PacketWriterUtils.writeAndSend(player, chunkDataPacket);
}
@Override @Override
protected void retrieveChunk(int chunkX, int chunkZ, Consumer<Chunk> callback) { protected void retrieveChunk(int chunkX, int chunkZ, Consumer<Chunk> callback) {
if (folder != null) { if (folder != null) {
@ -184,9 +196,12 @@ public class InstanceContainer extends Instance {
} }
public void sendChunkUpdate(Chunk chunk) { public void sendChunkUpdate(Chunk chunk) {
Set<Player> chunkViewers = chunk.getViewers();
if (!chunkViewers.isEmpty()) {
sendChunkUpdate(chunkViewers, chunk);
}
// Update for players in this instance // Update for players in this instance
if (!getPlayers().isEmpty()) /*if (!getPlayers().isEmpty())
sendChunkUpdate(getPlayers(), chunk); sendChunkUpdate(getPlayers(), chunk);
// Update for shared instances // Update for shared instances
@ -195,7 +210,7 @@ public class InstanceContainer extends Instance {
Set<Player> instancePlayers = sharedInstance.getPlayers(); Set<Player> instancePlayers = sharedInstance.getPlayers();
if (!instancePlayers.isEmpty()) if (!instancePlayers.isEmpty())
sendChunkUpdate(instancePlayers, chunk); sendChunkUpdate(instancePlayers, chunk);
}); });*/
} }
@Override @Override
@ -209,7 +224,7 @@ public class InstanceContainer extends Instance {
public void sendChunk(Player player, Chunk chunk) { public void sendChunk(Player player, Chunk chunk) {
/*Buffer data = chunk.getFullDataPacket(); /*Buffer data = chunk.getFullDataPacket();
if(data == null) { if(data == null) {
PacketWriter.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> { PacketWriterUtils.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> {
chunk.setFullDataPacket(buffer); chunk.setFullDataPacket(buffer);
sendChunkUpdate(player, chunk); sendChunkUpdate(player, chunk);
}); });
@ -256,4 +271,11 @@ public class InstanceContainer extends Instance {
this.folder = folder; this.folder = folder;
} }
private void sendBlockChange(Chunk chunk, int x, int y, int z, short blockId) {
BlockChangePacket blockChangePacket = new BlockChangePacket();
blockChangePacket.blockPosition = new BlockPosition(x, y, z);
blockChangePacket.blockId = blockId;
chunk.sendPacketToViewers(blockChangePacket);
}
} }

View File

@ -80,6 +80,11 @@ public class SharedInstance extends Instance {
instanceContainer.sendChunkUpdate(player, chunk); instanceContainer.sendChunkUpdate(player, chunk);
} }
@Override
public void sendChunkSectionUpdate(Chunk chunk, int section, Player player) {
instanceContainer.sendChunkSectionUpdate(chunk, section, player);
}
@Override @Override
public void retrieveChunk(int chunkX, int chunkZ, Consumer<Chunk> callback) { public void retrieveChunk(int chunkX, int chunkZ, Consumer<Chunk> callback) {
instanceContainer.retrieveChunk(chunkX, chunkZ, callback); instanceContainer.retrieveChunk(chunkX, chunkZ, callback);

View File

@ -9,6 +9,7 @@ import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.net.packet.client.play.ClientPlayerBlockPlacementPacket; import fr.themode.minestom.net.packet.client.play.ClientPlayerBlockPlacementPacket;
import fr.themode.minestom.net.packet.client.play.ClientPlayerDiggingPacket; import fr.themode.minestom.net.packet.client.play.ClientPlayerDiggingPacket;
import fr.themode.minestom.utils.BlockPosition; import fr.themode.minestom.utils.BlockPosition;
import fr.themode.minestom.utils.ChunkUtils;
public class BlockPlacementListener { public class BlockPlacementListener {
@ -23,15 +24,17 @@ public class BlockPlacementListener {
return; return;
ItemStack usedItem = hand == Player.Hand.MAIN ? playerInventory.getItemInMainHand() : playerInventory.getItemInOffHand(); ItemStack usedItem = hand == Player.Hand.MAIN ? playerInventory.getItemInMainHand() : playerInventory.getItemInOffHand();
if (!usedItem.getMaterial().isBlock()) if (!usedItem.getMaterial().isBlock()) {
//instance.setBlock(blockPosition.clone().add(0, 1, 0), (short) 10);
return; return;
}
int offsetX = blockFace == ClientPlayerDiggingPacket.BlockFace.WEST ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.EAST ? 1 : 0; int offsetX = blockFace == ClientPlayerDiggingPacket.BlockFace.WEST ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.EAST ? 1 : 0;
int offsetY = blockFace == ClientPlayerDiggingPacket.BlockFace.BOTTOM ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.TOP ? 1 : 0; int offsetY = blockFace == ClientPlayerDiggingPacket.BlockFace.BOTTOM ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.TOP ? 1 : 0;
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);
boolean intersectPlayer = player.getBoundingBox().intersect(blockPosition); boolean intersectPlayer = player.getBoundingBox().intersect(blockPosition); // TODO check if collide with nearby players
if (!intersectPlayer) { if (!intersectPlayer) {
PlayerBlockPlaceEvent playerBlockPlaceEvent = new PlayerBlockPlaceEvent((short) 10, blockPosition, packet.hand); PlayerBlockPlaceEvent playerBlockPlaceEvent = new PlayerBlockPlaceEvent((short) 10, blockPosition, packet.hand);
player.callEvent(PlayerBlockPlaceEvent.class, playerBlockPlaceEvent); player.callEvent(PlayerBlockPlaceEvent.class, playerBlockPlaceEvent);
@ -49,7 +52,7 @@ public class BlockPlacementListener {
} }
} else { } else {
Chunk chunk = instance.getChunkAt(blockPosition); Chunk chunk = instance.getChunkAt(blockPosition);
instance.sendChunkUpdate(player, chunk); instance.sendChunkSectionUpdate(chunk, ChunkUtils.getSectionAt(blockPosition.getY()), player);
} }
} }
player.getInventory().refreshSlot(player.getHeldSlot()); player.getInventory().refreshSlot(player.getHeldSlot());

View File

@ -25,7 +25,7 @@ public class PlayerDiggingListener {
if (player.getGameMode() == GameMode.CREATIVE) { if (player.getGameMode() == GameMode.CREATIVE) {
Instance instance = player.getInstance(); Instance instance = player.getInstance();
if (instance != null) { if (instance != null) {
instance.setBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), (short) 0); instance.breakBlock(player, blockPosition);
} }
} else if (player.getGameMode() == GameMode.SURVIVAL) { } else if (player.getGameMode() == GameMode.SURVIVAL) {
Instance instance = player.getInstance(); Instance instance = player.getInstance();

View File

@ -5,6 +5,7 @@ import fr.adamaq01.ozao.net.packet.Packet;
import fr.themode.minestom.Main; import fr.themode.minestom.Main;
import fr.themode.minestom.entity.Player; import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.packet.server.ServerPacket; import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.net.player.PlayerConnection;
import fr.themode.minestom.utils.PacketUtils; import fr.themode.minestom.utils.PacketUtils;
import java.util.Collection; import java.util.Collection;
@ -25,13 +26,13 @@ public class PacketWriterUtils {
public static void writeAndSend(Collection<Player> players, ServerPacket serverPacket) { public static void writeAndSend(Collection<Player> players, ServerPacket serverPacket) {
batchesPool.execute(() -> { batchesPool.execute(() -> {
Packet p = PacketUtils.writePacket(serverPacket);
Buffer encoded = PacketUtils.encode(p);
int size = players.size(); int size = players.size();
if (size == 0) if (size == 0)
return; return;
Packet p = PacketUtils.writePacket(serverPacket);
Buffer encoded = PacketUtils.encode(p);
encoded.getData().retain(size).markReaderIndex(); encoded.getData().retain(size).markReaderIndex();
for (Player player : players) { for (Player player : players) {
player.getPlayerConnection().writeUnencodedPacket(encoded); player.getPlayerConnection().writeUnencodedPacket(encoded);
@ -40,4 +41,16 @@ public class PacketWriterUtils {
}); });
} }
public static void writeAndSend(PlayerConnection playerConnection, ServerPacket serverPacket) {
batchesPool.execute(() -> {
Packet p = PacketUtils.writePacket(serverPacket);
Buffer encoded = PacketUtils.encode(p);
playerConnection.writeUnencodedPacket(encoded);
});
}
public static void writeAndSend(Player player, ServerPacket serverPacket) {
writeAndSend(player.getPlayerConnection(), serverPacket);
}
} }

View File

@ -20,6 +20,7 @@ public class ChunkDataPacket implements ServerPacket {
public boolean fullChunk; public boolean fullChunk;
public Chunk chunk; public Chunk chunk;
public int[] sections;
@Override @Override
public void write(PacketWriter writer) { public void write(PacketWriter writer) {
@ -31,10 +32,11 @@ public class ChunkDataPacket implements ServerPacket {
int mask = 0; int mask = 0;
Buffer blocks = Buffer.create(); Buffer blocks = Buffer.create();
for (int i = 0; i < 16; i++) { for (int i = 0; i < 16; i++) {
// TODO if fullchunk is false then only send changed sections if (fullChunk || (sections.length == 16 && sections[i] != 0)) {
mask |= 1 << i; mask |= 1 << i;
short[] section = getSection(chunk, i); short[] section = getSection(chunk, i);
Utils.writeBlocks(blocks, section, 14); Utils.writeBlocks(blocks, section, 14);
}
} }
// Biome data // Biome data
if (fullChunk) { if (fullChunk) {

View File

@ -4,8 +4,8 @@ import fr.adamaq01.ozao.net.Buffer;
import fr.adamaq01.ozao.net.packet.Packet; import fr.adamaq01.ozao.net.packet.Packet;
import fr.adamaq01.ozao.net.server.Connection; import fr.adamaq01.ozao.net.server.Connection;
import fr.themode.minestom.net.ConnectionState; import fr.themode.minestom.net.ConnectionState;
import fr.themode.minestom.net.PacketWriterUtils;
import fr.themode.minestom.net.packet.server.ServerPacket; import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.PacketUtils;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.SocketChannel;
@ -27,9 +27,9 @@ public class PlayerConnection {
// TODO make that proper (remove reflection) // TODO make that proper (remove reflection)
private Field field; private static Field field;
{ static {
try { try {
field = Class.forName("fr.adamaq01.ozao.net.server.backend.tcp.TCPConnection").getDeclaredField("channel"); field = Class.forName("fr.adamaq01.ozao.net.server.backend.tcp.TCPConnection").getDeclaredField("channel");
} catch (NoSuchFieldException e) { } catch (NoSuchFieldException e) {
@ -49,7 +49,7 @@ public class PlayerConnection {
} }
public void sendPacket(ServerPacket serverPacket) { public void sendPacket(ServerPacket serverPacket) {
sendPacket(PacketUtils.writePacket(serverPacket)); PacketWriterUtils.writeAndSend(this, serverPacket);
} }
public void flush() { public void flush() {

View File

@ -18,6 +18,10 @@ public class ChunkUtils {
return new int[]{chunkX, chunkZ}; return new int[]{chunkX, chunkZ};
} }
public static int getSectionAt(int y) {
return y / 16;
}
public static long[] getChunksInRange(final Position position, int range) { public static long[] getChunksInRange(final Position position, int range) {
long[] visibleChunks = new long[MathUtils.square(range + 1)]; long[] visibleChunks = new long[MathUtils.square(range + 1)];