mirror of
https://github.com/Minestom/Minestom.git
synced 2024-12-27 03:27:56 +01:00
Basic instances implementation
This commit is contained in:
parent
383bb634f4
commit
d1967186e5
@ -7,12 +7,12 @@ 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.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.InstanceManager;
|
||||||
import fr.themode.minestom.net.ConnectionManager;
|
import fr.themode.minestom.net.ConnectionManager;
|
||||||
import fr.themode.minestom.net.PacketProcessor;
|
import fr.themode.minestom.net.PacketProcessor;
|
||||||
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.KeepAlivePacket;
|
import fr.themode.minestom.net.packet.server.play.KeepAlivePacket;
|
||||||
import fr.themode.minestom.net.packet.server.play.PlayerInfoPacket;
|
import fr.themode.minestom.net.packet.server.play.PlayerInfoPacket;
|
||||||
import fr.themode.minestom.net.player.PlayerConnection;
|
|
||||||
import fr.themode.minestom.net.protocol.MinecraftProtocol;
|
import fr.themode.minestom.net.protocol.MinecraftProtocol;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
@ -25,12 +25,14 @@ public class Main {
|
|||||||
private static Server server;
|
private static Server server;
|
||||||
|
|
||||||
// In-Game Manager
|
// In-Game Manager
|
||||||
|
private static InstanceManager instanceManager;
|
||||||
private static EntityManager entityManager;
|
private static EntityManager entityManager;
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
connectionManager = new ConnectionManager();
|
connectionManager = new ConnectionManager();
|
||||||
packetProcessor = new PacketProcessor();
|
packetProcessor = new PacketProcessor();
|
||||||
|
|
||||||
|
instanceManager = new InstanceManager();
|
||||||
entityManager = new EntityManager();
|
entityManager = new EntityManager();
|
||||||
|
|
||||||
server = new TCPServer(new MinecraftProtocol()).addHandler(new ServerHandler() {
|
server = new TCPServer(new MinecraftProtocol()).addHandler(new ServerHandler() {
|
||||||
@ -51,7 +53,7 @@ public class Main {
|
|||||||
PlayerInfoPacket playerInfoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.REMOVE_PLAYER);
|
PlayerInfoPacket playerInfoPacket = new PlayerInfoPacket(PlayerInfoPacket.Action.REMOVE_PLAYER);
|
||||||
playerInfoPacket.playerInfos.add(new PlayerInfoPacket.RemovePlayer(player.getUuid()));
|
playerInfoPacket.playerInfos.add(new PlayerInfoPacket.RemovePlayer(player.getUuid()));
|
||||||
DestroyEntitiesPacket destroyEntitiesPacket = new DestroyEntitiesPacket();
|
DestroyEntitiesPacket destroyEntitiesPacket = new DestroyEntitiesPacket();
|
||||||
destroyEntitiesPacket.entityIds = new int[] {player.getEntityId()};
|
destroyEntitiesPacket.entityIds = new int[]{player.getEntityId()};
|
||||||
for (Player onlinePlayer : connectionManager.getOnlinePlayers()) {
|
for (Player onlinePlayer : connectionManager.getOnlinePlayers()) {
|
||||||
if (!onlinePlayer.equals(player)) {
|
if (!onlinePlayer.equals(player)) {
|
||||||
onlinePlayer.getPlayerConnection().sendPacket(destroyEntitiesPacket);
|
onlinePlayer.getPlayerConnection().sendPacket(destroyEntitiesPacket);
|
||||||
@ -117,6 +119,10 @@ public class Main {
|
|||||||
return server;
|
return server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static InstanceManager getInstanceManager() {
|
||||||
|
return instanceManager;
|
||||||
|
}
|
||||||
|
|
||||||
public static EntityManager getEntityManager() {
|
public static EntityManager getEntityManager() {
|
||||||
return entityManager;
|
return entityManager;
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,23 @@
|
|||||||
package fr.themode.minestom.entity;
|
package fr.themode.minestom.entity;
|
||||||
|
|
||||||
import fr.themode.minestom.Main;
|
import fr.themode.minestom.instance.Chunk;
|
||||||
|
import fr.themode.minestom.instance.Instance;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class Entity {
|
public class Entity {
|
||||||
|
|
||||||
private static volatile int lastEntityId;
|
private static volatile int lastEntityId;
|
||||||
|
protected Instance instance;
|
||||||
|
protected double lastX, lastY, lastZ;
|
||||||
protected double x, y, z;
|
protected double x, y, z;
|
||||||
private int id;
|
private int id;
|
||||||
protected UUID uuid;
|
protected UUID uuid;
|
||||||
private boolean isActive; // False if entity has only been instanced without being added somewhere
|
private boolean isActive; // False if entity has only been instanced without being added somewhere
|
||||||
private boolean shouldRemove;
|
private boolean shouldRemove;
|
||||||
|
|
||||||
|
private Object monitor = new Object();
|
||||||
|
|
||||||
public Entity() {
|
public Entity() {
|
||||||
this.id = generateId();
|
this.id = generateId();
|
||||||
this.uuid = UUID.randomUUID();
|
this.uuid = UUID.randomUUID();
|
||||||
@ -34,18 +39,44 @@ public class Entity {
|
|||||||
return isActive;
|
return isActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addToWorld() {
|
public Instance getInstance() {
|
||||||
this.isActive = true;
|
return instance;
|
||||||
EntityManager entityManager = Main.getEntityManager();
|
|
||||||
if (this instanceof EntityCreature) {
|
|
||||||
entityManager.addCreature((EntityCreature) this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPosition(double x, double y, double z) {
|
public void setInstance(Instance instance) {
|
||||||
|
if (instance == null)
|
||||||
|
throw new IllegalArgumentException("instance cannot be null!");
|
||||||
|
|
||||||
|
if (this.instance != null) {
|
||||||
|
this.instance.removeEntity(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isActive = true;
|
||||||
|
this.instance = instance;
|
||||||
|
instance.addEntity(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refreshPosition(double x, double y, double z) {
|
||||||
|
this.lastX = this.x;
|
||||||
|
this.lastY = this.y;
|
||||||
|
this.lastZ = this.z;
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.z = z;
|
this.z = z;
|
||||||
|
|
||||||
|
Instance instance = getInstance();
|
||||||
|
if (instance != null) {
|
||||||
|
Chunk lastChunk = instance.getChunkAt(lastX, lastZ);
|
||||||
|
Chunk newChunk = instance.getChunkAt(x, z);
|
||||||
|
if (newChunk != null && lastChunk != newChunk) {
|
||||||
|
synchronized (lastChunk) {
|
||||||
|
synchronized (newChunk) {
|
||||||
|
lastChunk.removeEntity(this);
|
||||||
|
newChunk.addEntity(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public double getX() {
|
public double getX() {
|
||||||
|
@ -40,10 +40,13 @@ public abstract class EntityCreature extends LivingEntity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void removeViewer(Player player) {
|
public void removeViewer(Player player) {
|
||||||
|
synchronized (viewers) {
|
||||||
if (!viewers.contains(player))
|
if (!viewers.contains(player))
|
||||||
return;
|
return;
|
||||||
|
this.viewers.remove(player);
|
||||||
// TODO send packet to remove entity
|
// TODO send packet to remove entity
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Set<Player> getViewers() {
|
public Set<Player> getViewers() {
|
||||||
return Collections.unmodifiableSet(viewers);
|
return Collections.unmodifiableSet(viewers);
|
||||||
|
@ -1,47 +1,45 @@
|
|||||||
package fr.themode.minestom.entity;
|
package fr.themode.minestom.entity;
|
||||||
|
|
||||||
import fr.themode.minestom.Main;
|
import fr.themode.minestom.Main;
|
||||||
import fr.themode.minestom.net.ConnectionManager;
|
import fr.themode.minestom.instance.Instance;
|
||||||
|
import fr.themode.minestom.instance.InstanceManager;
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class EntityManager {
|
public class EntityManager {
|
||||||
|
|
||||||
private Set<EntityCreature> creatures = Collections.synchronizedSet(new HashSet<>());
|
private static InstanceManager instanceManager = Main.getInstanceManager();
|
||||||
|
|
||||||
private ExecutorService creaturesPool = Executors.newFixedThreadPool(3);
|
private ExecutorService creaturesPool = Executors.newFixedThreadPool(3);
|
||||||
private ExecutorService playersPool = Executors.newFixedThreadPool(2);
|
private ExecutorService playersPool = Executors.newFixedThreadPool(2);
|
||||||
|
|
||||||
private ConnectionManager connectionManager = Main.getConnectionManager();
|
|
||||||
|
|
||||||
public void update() {
|
public void update() {
|
||||||
creatures.removeIf(creature -> creature.shouldRemove());
|
|
||||||
|
|
||||||
synchronized (creatures) {
|
for (Instance instance : instanceManager.getInstances()) {
|
||||||
Iterator<EntityCreature> iterator = creatures.iterator();
|
// Creatures
|
||||||
while (iterator.hasNext()) {
|
for (EntityCreature creature : instance.getCreatures()) {
|
||||||
EntityCreature creature = iterator.next();
|
creaturesPool.submit(() -> {
|
||||||
creaturesPool.submit(creature::update);
|
creature.update();
|
||||||
|
boolean shouldRemove = creature.shouldRemove();
|
||||||
|
if (shouldRemove) {
|
||||||
|
instance.removeEntity(creature);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Players
|
||||||
|
for (Player player : instance.getPlayers()) {
|
||||||
|
playersPool.submit(() -> {
|
||||||
|
player.update();
|
||||||
|
boolean shouldRemove = player.shouldRemove();
|
||||||
|
if (shouldRemove) {
|
||||||
|
instance.removeEntity(player);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Collection<Player> players = connectionManager.getOnlinePlayers();
|
|
||||||
Iterator<Player> iterator = players.iterator();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
Player player = iterator.next();
|
|
||||||
playersPool.submit(player::update);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<EntityCreature> getCreatures() {
|
|
||||||
return Collections.unmodifiableSet(creatures);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void addCreature(EntityCreature creature) {
|
|
||||||
this.creatures.add(creature);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
package fr.themode.minestom.entity;
|
package fr.themode.minestom.entity;
|
||||||
|
|
||||||
import fr.themode.minestom.Main;
|
import fr.themode.minestom.Main;
|
||||||
import fr.themode.minestom.net.packet.server.play.EntityLookAndRelativeMovePacket;
|
|
||||||
import fr.themode.minestom.net.packet.server.play.EntityTeleportPacket;
|
import fr.themode.minestom.net.packet.server.play.EntityTeleportPacket;
|
||||||
import fr.themode.minestom.net.packet.server.play.PlayerPositionAndLookPacket;
|
|
||||||
import fr.themode.minestom.net.player.PlayerConnection;
|
import fr.themode.minestom.net.player.PlayerConnection;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -50,12 +48,6 @@ public class Player extends LivingEntity {
|
|||||||
return playerConnection;
|
return playerConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshPosition(double x, double y, double z) {
|
|
||||||
this.x = x;
|
|
||||||
this.y = y;
|
|
||||||
this.z = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void refreshView(float yaw, float pitch) {
|
public void refreshView(float yaw, float pitch) {
|
||||||
this.yaw = yaw;
|
this.yaw = yaw;
|
||||||
this.pitch = pitch;
|
this.pitch = pitch;
|
||||||
|
@ -13,13 +13,13 @@ public class ChickenCreature extends EntityCreature {
|
|||||||
public void update() {
|
public void update() {
|
||||||
onGround = true;
|
onGround = true;
|
||||||
|
|
||||||
double speed = 0.05;
|
double speed = 0.075;
|
||||||
double newPos = getZ() + speed;
|
double newPos = getZ() + speed;
|
||||||
|
|
||||||
EntityRelativeMovePacket entityRelativeMovePacket = new EntityRelativeMovePacket();
|
EntityRelativeMovePacket entityRelativeMovePacket = new EntityRelativeMovePacket();
|
||||||
entityRelativeMovePacket.entityId = getEntityId();
|
entityRelativeMovePacket.entityId = getEntityId();
|
||||||
entityRelativeMovePacket.deltaZ = (short) ((newPos * 32 - getZ() * 32) * 128);
|
entityRelativeMovePacket.deltaZ = (short) ((newPos * 32 - getZ() * 32) * 128);
|
||||||
entityRelativeMovePacket.onGround = false;
|
entityRelativeMovePacket.onGround = true;
|
||||||
getViewers().forEach(player -> player.getPlayerConnection().sendPacket(entityRelativeMovePacket));
|
getViewers().forEach(player -> player.getPlayerConnection().sendPacket(entityRelativeMovePacket));
|
||||||
setZ(newPos);
|
setZ(newPos);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package fr.themode.minestom.world;
|
package fr.themode.minestom.instance;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public enum CustomBiome {
|
public enum Biome {
|
||||||
|
|
||||||
OCEAN(0),
|
OCEAN(0),
|
||||||
PLAINS(1),
|
PLAINS(1),
|
||||||
@ -10,11 +10,11 @@ public enum CustomBiome {
|
|||||||
|
|
||||||
private int id;
|
private int id;
|
||||||
|
|
||||||
CustomBiome(int id) {
|
Biome(int id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CustomBiome fromId(int id) {
|
public static Biome fromId(int id) {
|
||||||
return Arrays.stream(values()).filter(customBiome -> customBiome.id == id).findFirst().get();
|
return Arrays.stream(values()).filter(customBiome -> customBiome.id == id).findFirst().get();
|
||||||
}
|
}
|
||||||
|
|
@ -1,15 +1,15 @@
|
|||||||
package fr.themode.minestom.world;
|
package fr.themode.minestom.instance;
|
||||||
|
|
||||||
public class CustomBlock {
|
public class Block {
|
||||||
|
|
||||||
private short typeAndDamage;
|
private short typeAndDamage;
|
||||||
|
|
||||||
public CustomBlock(int type) {
|
public Block(int type) {
|
||||||
this.typeAndDamage = (short) (type & 0x0FFF);
|
this.typeAndDamage = (short) (type & 0x0FFF);
|
||||||
this.typeAndDamage |= (0 << 12) & 0xF000;
|
this.typeAndDamage |= (0 << 12) & 0xF000;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CustomBlock(int type, int damage) {
|
public Block(int type, int damage) {
|
||||||
this.typeAndDamage = (short) (type & 0x0FFF);
|
this.typeAndDamage = (short) (type & 0x0FFF);
|
||||||
this.typeAndDamage |= (damage << 12) & 0xF000;
|
this.typeAndDamage |= (damage << 12) & 0xF000;
|
||||||
}
|
}
|
104
src/main/java/fr/themode/minestom/instance/Chunk.java
Normal file
104
src/main/java/fr/themode/minestom/instance/Chunk.java
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
package fr.themode.minestom.instance;
|
||||||
|
|
||||||
|
import fr.themode.minestom.entity.Entity;
|
||||||
|
import fr.themode.minestom.entity.EntityCreature;
|
||||||
|
import fr.themode.minestom.entity.Player;
|
||||||
|
import fr.themode.minestom.net.packet.server.play.ChunkDataPacket;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class Chunk {
|
||||||
|
|
||||||
|
protected Set<EntityCreature> creatures = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
protected Set<Player> players = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
private int chunkX, chunkZ;
|
||||||
|
private Biome biome;
|
||||||
|
private HashMap<Short, Block> blocks = new HashMap<>();
|
||||||
|
private ChunkDataPacket fullChunkPacket;
|
||||||
|
|
||||||
|
public Chunk(Biome biome, int chunkX, int chunkZ) {
|
||||||
|
this.biome = biome;
|
||||||
|
this.chunkX = chunkX;
|
||||||
|
this.chunkZ = chunkZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlock(int x, int y, int z, Block block) {
|
||||||
|
short index = (short) (x & 0x000F);
|
||||||
|
index |= (y << 4) & 0x0FF0;
|
||||||
|
index |= (z << 12) & 0xF000;
|
||||||
|
this.blocks.put(index, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Block getBlock(int x, int y, int z) {
|
||||||
|
short index = (short) (x & 0x000F);
|
||||||
|
index |= (y << 4) & 0x0FF0;
|
||||||
|
index |= (z << 12) & 0xF000;
|
||||||
|
return this.blocks.getOrDefault(index, new Block(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEntity(Entity entity) {
|
||||||
|
if (entity instanceof Player) {
|
||||||
|
synchronized (players) {
|
||||||
|
if (this.players.contains(entity))
|
||||||
|
return;
|
||||||
|
this.players.add((Player) entity);
|
||||||
|
}
|
||||||
|
} else if (entity instanceof EntityCreature) {
|
||||||
|
synchronized (creatures) {
|
||||||
|
if (this.creatures.contains(entity))
|
||||||
|
return;
|
||||||
|
this.creatures.add((EntityCreature) entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeEntity(Entity entity) {
|
||||||
|
if (entity instanceof Player) {
|
||||||
|
synchronized (players) {
|
||||||
|
this.players.remove(entity);
|
||||||
|
}
|
||||||
|
} else if (entity instanceof EntityCreature) {
|
||||||
|
synchronized (creatures) {
|
||||||
|
this.creatures.remove(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public HashMap<Short, Block> getBlocks() {
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Biome getBiome() {
|
||||||
|
return biome;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getChunkX() {
|
||||||
|
return chunkX;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getChunkZ() {
|
||||||
|
return chunkZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<EntityCreature> getCreatures() {
|
||||||
|
return Collections.unmodifiableSet(creatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Player> getPlayers() {
|
||||||
|
return Collections.unmodifiableSet(players);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void refreshFullChunkPacket() {
|
||||||
|
ChunkDataPacket chunkDataPacket = new ChunkDataPacket();
|
||||||
|
chunkDataPacket.fullChunk = true;
|
||||||
|
chunkDataPacket.chunk = this;
|
||||||
|
// TODO fill buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkDataPacket getFullChunkPacket() {
|
||||||
|
return fullChunkPacket;
|
||||||
|
}
|
||||||
|
}
|
110
src/main/java/fr/themode/minestom/instance/Instance.java
Normal file
110
src/main/java/fr/themode/minestom/instance/Instance.java
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
package fr.themode.minestom.instance;
|
||||||
|
|
||||||
|
import fr.themode.minestom.entity.Entity;
|
||||||
|
import fr.themode.minestom.entity.EntityCreature;
|
||||||
|
import fr.themode.minestom.entity.Player;
|
||||||
|
import fr.themode.minestom.utils.GroupedCollections;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class Instance {
|
||||||
|
|
||||||
|
private int id;
|
||||||
|
|
||||||
|
private Set<Chunk> chunksSet = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
|
||||||
|
public Instance(int id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO BlockBatch with pool
|
||||||
|
public void setBlock(int x, int y, int z, Block block) {
|
||||||
|
Chunk chunk = getChunkAt(x, z);
|
||||||
|
if (chunk == null) {
|
||||||
|
int chunkX = x / 16;
|
||||||
|
int chunkZ = z / 16;
|
||||||
|
chunk = new Chunk(Biome.VOID, chunkX, chunkZ);
|
||||||
|
this.chunksSet.add(chunk);
|
||||||
|
}
|
||||||
|
synchronized (chunk) {
|
||||||
|
chunk.setBlock(x % 16, y, z % 16, block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Chunk getChunk(int chunkX, int chunkZ) {
|
||||||
|
for (Chunk chunk : getChunks()) {
|
||||||
|
if (chunk.getChunkX() == chunkX && chunk.getChunkZ() == chunkZ)
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Chunk getChunkAt(double x, double z) {
|
||||||
|
int chunkX = (int) (x / 16);
|
||||||
|
int chunkZ = (int) (z / 16);
|
||||||
|
return getChunk(chunkX, chunkZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Chunk> getChunks() {
|
||||||
|
return Collections.unmodifiableSet(chunksSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEntity(Entity entity) {
|
||||||
|
Instance lastInstance = entity.getInstance();
|
||||||
|
if (lastInstance != null && lastInstance != this) {
|
||||||
|
lastInstance.removeEntity(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity instanceof EntityCreature) {
|
||||||
|
// TODO based on distance with player
|
||||||
|
getPlayers().forEach(p -> ((EntityCreature) entity).addViewer(p));
|
||||||
|
} else if (entity instanceof Player) {
|
||||||
|
getCreatures().forEach(entityCreature -> entityCreature.addViewer((Player) entity));
|
||||||
|
}
|
||||||
|
|
||||||
|
Chunk chunk = getChunkAt(entity.getX(), entity.getZ());
|
||||||
|
chunk.addEntity(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeEntity(Entity entity) {
|
||||||
|
Instance entityInstance = entity.getInstance();
|
||||||
|
if (entityInstance == null || entityInstance != this)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (entity instanceof EntityCreature) {
|
||||||
|
EntityCreature creature = (EntityCreature) entity;
|
||||||
|
creature.getViewers().forEach(p -> creature.removeViewer(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
Chunk chunk = getChunkAt(entity.getX(), entity.getZ());
|
||||||
|
chunk.removeEntity(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupedCollections<EntityCreature> getCreatures() {
|
||||||
|
GroupedCollections<EntityCreature> creatures = new GroupedCollections();
|
||||||
|
for (Chunk chunk : getChunks()) {
|
||||||
|
creatures.addCollection(chunk.creatures);
|
||||||
|
}
|
||||||
|
return creatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupedCollections<Player> getPlayers() {
|
||||||
|
GroupedCollections<Player> players = new GroupedCollections();
|
||||||
|
for (Chunk chunk : getChunks()) {
|
||||||
|
players.addCollection(chunk.players);
|
||||||
|
}
|
||||||
|
return players;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Chunk createChunk(int chunkX, int chunkZ) {
|
||||||
|
Chunk chunk = new Chunk(Biome.VOID, chunkX, chunkZ);
|
||||||
|
this.chunksSet.add(chunk);
|
||||||
|
return chunk;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package fr.themode.minestom.instance;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class InstanceManager {
|
||||||
|
|
||||||
|
private static volatile int lastInstanceId;
|
||||||
|
|
||||||
|
private Set<Instance> instances = Collections.synchronizedSet(new HashSet<>());
|
||||||
|
|
||||||
|
private static int generateId() {
|
||||||
|
return ++lastInstanceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instance createInstance() {
|
||||||
|
Instance instance = new Instance(generateId());
|
||||||
|
this.instances.add(instance);
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Instance> getInstances() {
|
||||||
|
return Collections.unmodifiableSet(instances);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,22 +1,22 @@
|
|||||||
package fr.themode.minestom.net.packet.client.login;
|
package fr.themode.minestom.net.packet.client.login;
|
||||||
|
|
||||||
import fr.adamaq01.ozao.net.Buffer;
|
import fr.adamaq01.ozao.net.Buffer;
|
||||||
import fr.adamaq01.ozao.net.packet.Packet;
|
import fr.themode.minestom.Main;
|
||||||
import fr.themode.minestom.entity.GameMode;
|
import fr.themode.minestom.entity.GameMode;
|
||||||
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.ChickenCreature;
|
||||||
|
import fr.themode.minestom.instance.Block;
|
||||||
|
import fr.themode.minestom.instance.Chunk;
|
||||||
|
import fr.themode.minestom.instance.Instance;
|
||||||
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.PlayerInfoPacket;
|
import fr.themode.minestom.net.packet.server.play.*;
|
||||||
import fr.themode.minestom.net.packet.server.play.PlayerPositionAndLookPacket;
|
|
||||||
import fr.themode.minestom.net.packet.server.play.SpawnPlayerPacket;
|
|
||||||
import fr.themode.minestom.net.packet.server.play.SpawnPositionPacket;
|
|
||||||
import fr.themode.minestom.net.player.PlayerConnection;
|
import fr.themode.minestom.net.player.PlayerConnection;
|
||||||
import fr.themode.minestom.utils.Utils;
|
import fr.themode.minestom.utils.Utils;
|
||||||
import fr.themode.minestom.world.*;
|
import fr.themode.minestom.world.Dimension;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
@ -25,6 +25,16 @@ public class LoginStartPacket implements ClientPreplayPacket {
|
|||||||
|
|
||||||
private String username;
|
private String username;
|
||||||
|
|
||||||
|
// Test
|
||||||
|
private static Instance instance;
|
||||||
|
|
||||||
|
static {
|
||||||
|
instance = Main.getInstanceManager().createInstance();
|
||||||
|
for (int x = -64; x < 64; x++)
|
||||||
|
for (int z = -64; z < 64; z++)
|
||||||
|
instance.setBlock(x, 4, z, new Block(1));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void process(PlayerConnection connection, ConnectionManager connectionManager) {
|
public void process(PlayerConnection connection, ConnectionManager connectionManager) {
|
||||||
HashMap<String, UUID> uuids = new HashMap<>();
|
HashMap<String, UUID> uuids = new HashMap<>();
|
||||||
@ -41,15 +51,15 @@ public class LoginStartPacket implements ClientPreplayPacket {
|
|||||||
connection.setConnectionState(ConnectionState.PLAY);
|
connection.setConnectionState(ConnectionState.PLAY);
|
||||||
connectionManager.createPlayer(uuids.get(username), username, connection);
|
connectionManager.createPlayer(uuids.get(username), username, connection);
|
||||||
Player player = connectionManager.getPlayer(connection);
|
Player player = connectionManager.getPlayer(connection);
|
||||||
player.addToWorld();
|
|
||||||
|
|
||||||
// TODO complete login sequence with optionals packets
|
// TODO complete login sequence with optionals packets
|
||||||
JoinGamePacket joinGamePacket = new JoinGamePacket();
|
JoinGamePacket joinGamePacket = new JoinGamePacket();
|
||||||
joinGamePacket.entityId = player.getEntityId();
|
joinGamePacket.entityId = player.getEntityId();
|
||||||
joinGamePacket.gameMode = GameMode.CREATIVE;
|
joinGamePacket.gameMode = GameMode.CREATIVE;
|
||||||
joinGamePacket.dimension = Dimension.OVERWORLD;
|
joinGamePacket.dimension = Dimension.OVERWORLD;
|
||||||
joinGamePacket.maxPlayers = 10;
|
joinGamePacket.maxPlayers = 0; // Unused
|
||||||
joinGamePacket.levelType = "default";
|
joinGamePacket.levelType = "default";
|
||||||
|
joinGamePacket.viewDistance = 14;
|
||||||
joinGamePacket.reducedDebugInfo = false;
|
joinGamePacket.reducedDebugInfo = false;
|
||||||
connection.sendPacket(joinGamePacket);
|
connection.sendPacket(joinGamePacket);
|
||||||
|
|
||||||
@ -59,16 +69,12 @@ public class LoginStartPacket implements ClientPreplayPacket {
|
|||||||
|
|
||||||
// TODO player abilities
|
// TODO player abilities
|
||||||
|
|
||||||
CustomChunk customChunk = new CustomChunk(CustomBiome.VOID);
|
player.setInstance(instance);
|
||||||
for (int x = 0; x < 16; x++)
|
ChunkDataPacket chunkDataPacket = new ChunkDataPacket();
|
||||||
for (int z = 0; z < 16; z++)
|
chunkDataPacket.fullChunk = true;
|
||||||
customChunk.setBlock(x, 4, z, new CustomBlock(1)); // Stone
|
for (Chunk chunk : instance.getChunks()) {
|
||||||
|
chunkDataPacket.chunk = chunk;
|
||||||
for (int x = -2; x < 2; x++) {
|
connection.sendPacket(chunkDataPacket);
|
||||||
for (int z = -2; z < 2; z++) {
|
|
||||||
Packet packet = ChunkPacketCreator.create(x, z, customChunk, 0, 15);
|
|
||||||
connection.getConnection().sendPacket(packet);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -99,9 +105,8 @@ public class LoginStartPacket implements ClientPreplayPacket {
|
|||||||
for (int z = -2; z < 2; z++) {
|
for (int z = -2; z < 2; z++) {
|
||||||
// TODO test entity
|
// TODO test entity
|
||||||
ChickenCreature chickenCreature = new ChickenCreature();
|
ChickenCreature chickenCreature = new ChickenCreature();
|
||||||
chickenCreature.setPosition(0 + (double) x * 1, 5, 0 + (double) z * 1);
|
chickenCreature.refreshPosition(0 + (double) x * 1, 5, 0 + (double) z * 1);
|
||||||
connectionManager.getOnlinePlayers().forEach(p -> chickenCreature.addViewer(p));
|
instance.addEntity(chickenCreature);
|
||||||
chickenCreature.addToWorld();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ public class JoinGamePacket implements ServerPacket {
|
|||||||
public Dimension dimension = Dimension.OVERWORLD;
|
public Dimension dimension = Dimension.OVERWORLD;
|
||||||
public byte maxPlayers = 0; // Unused
|
public byte maxPlayers = 0; // Unused
|
||||||
public String levelType = "default";
|
public String levelType = "default";
|
||||||
|
public int viewDistance;
|
||||||
public boolean reducedDebugInfo = false;
|
public boolean reducedDebugInfo = false;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -26,7 +27,7 @@ public class JoinGamePacket implements ServerPacket {
|
|||||||
buffer.putInt(dimension.getId());
|
buffer.putInt(dimension.getId());
|
||||||
buffer.putByte(maxPlayers);
|
buffer.putByte(maxPlayers);
|
||||||
Utils.writeString(buffer, levelType);
|
Utils.writeString(buffer, levelType);
|
||||||
Utils.writeVarInt(buffer, 8);
|
Utils.writeVarInt(buffer, viewDistance);
|
||||||
buffer.putBoolean(reducedDebugInfo);
|
buffer.putBoolean(reducedDebugInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,124 +1,93 @@
|
|||||||
package fr.themode.minestom.net.packet.server.play;
|
package fr.themode.minestom.net.packet.server.play;
|
||||||
|
|
||||||
import fr.adamaq01.ozao.net.Buffer;
|
import fr.adamaq01.ozao.net.Buffer;
|
||||||
|
import fr.themode.minestom.instance.Block;
|
||||||
|
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.Utils;
|
import fr.themode.minestom.utils.Utils;
|
||||||
|
import net.querz.nbt.CompoundTag;
|
||||||
|
import net.querz.nbt.LongArrayTag;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
public class ChunkDataPacket implements ServerPacket {
|
public class ChunkDataPacket implements ServerPacket {
|
||||||
|
|
||||||
public int columnX;
|
|
||||||
public int columnZ;
|
|
||||||
public boolean fullChunk;
|
public boolean fullChunk;
|
||||||
public int mask;
|
public Chunk chunk;
|
||||||
public ChunkSection[] chunkSections;
|
|
||||||
public int tileEntitesSize;
|
|
||||||
// TODO nbt tile entities
|
// TODO nbt tile entities
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(Buffer buffer) {
|
public void write(Buffer buffer) {
|
||||||
buffer.putInt(columnX);
|
buffer.putInt(chunk.getChunkX());
|
||||||
buffer.putInt(columnZ);
|
buffer.putInt(chunk.getChunkZ());
|
||||||
buffer.putBoolean(fullChunk);
|
buffer.putBoolean(fullChunk);
|
||||||
|
|
||||||
|
|
||||||
|
int mask = 0;
|
||||||
|
Buffer blocks = Buffer.create();
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
// TODO if fullchunk is false then only send changed sections
|
||||||
|
mask |= 1 << i;
|
||||||
|
Block[] section = getSection(chunk, i);
|
||||||
|
Utils.writeBlocks(blocks, section, 14);
|
||||||
|
}
|
||||||
|
// Biome data
|
||||||
|
if (fullChunk) {
|
||||||
|
int[] biomeData = new int[256];
|
||||||
|
for (int z = 0; z < 16; z++) {
|
||||||
|
for (int x = 0; x < 16; x++) {
|
||||||
|
biomeData[z * 16 | x] = chunk.getBiome().getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < biomeData.length; i++) {
|
||||||
|
blocks.putInt(biomeData[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Utils.writeVarInt(buffer, mask);
|
Utils.writeVarInt(buffer, mask);
|
||||||
|
|
||||||
// Nbt
|
// Heightmap
|
||||||
buffer.putByte((byte) 10);
|
int[] motionBlocking = new int[16 * 16];
|
||||||
buffer.putShort((short) 0);
|
int[] worldSurface = new int[16 * 16];
|
||||||
buffer.putByte((byte) 12);
|
for (int x = 0; x < 16; x++) {
|
||||||
buffer.putShort((short) "MOTION_BLOCKING".length());
|
for (int z = 0; z < 16; z++) {
|
||||||
|
motionBlocking[x + z * 16] = 4;
|
||||||
|
worldSurface[x + z * 16] = 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CompoundTag compound = new CompoundTag();
|
||||||
|
compound.put("MOTION_BLOCKING", new LongArrayTag(Utils.encodeBlocks(motionBlocking, 9)));
|
||||||
|
compound.put("WORLD_SURFACE", new LongArrayTag(Utils.encodeBlocks(worldSurface, 9)));
|
||||||
|
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
try {
|
try {
|
||||||
buffer.putBytes("MOTION_BLOCKING".getBytes("UTF-8"));
|
compound.serialize(new DataOutputStream(outputStream), 100);
|
||||||
} catch (UnsupportedEncodingException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
buffer.putInt(256);
|
byte[] data = outputStream.toByteArray();
|
||||||
for (int i = 0; i < 256; i++) {
|
buffer.putBytes(data);
|
||||||
buffer.putLong(Long.MAX_VALUE);
|
|
||||||
|
Utils.writeVarInt(buffer, blocks.length());
|
||||||
|
buffer.putBuffer(blocks);
|
||||||
|
Utils.writeVarInt(buffer, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.putByte((byte) 12);
|
private Block[] getSection(Chunk chunk, int section) {
|
||||||
buffer.putShort((short) "WORLD_SURFACE".length());
|
Block[] blocks = new Block[16 * 16 * 16];
|
||||||
try {
|
for (int y = 0; y < 16; y++) {
|
||||||
buffer.putBytes("WORLD_SURFACE".getBytes("UTF-8"));
|
for (int x = 0; x < 16; x++) {
|
||||||
} catch (UnsupportedEncodingException e) {
|
for (int z = 0; z < 16; z++) {
|
||||||
e.printStackTrace();
|
int index = (((y * 16) + x) * 16) + z;
|
||||||
|
blocks[index] = chunk.getBlock(x, y + 16 * section, z);
|
||||||
}
|
}
|
||||||
buffer.putInt(256);
|
|
||||||
for (int i = 0; i < 256; i++) {
|
|
||||||
buffer.putLong(Long.MAX_VALUE);
|
|
||||||
}
|
}
|
||||||
buffer.putByte((byte) 0); // End nbt
|
|
||||||
|
|
||||||
Utils.writeVarInt(buffer, getDataSize());
|
|
||||||
writeData(buffer);
|
|
||||||
Utils.writeVarInt(buffer, tileEntitesSize);
|
|
||||||
// TODO nbt tile entities
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getDataSize() {
|
|
||||||
int result = Integer.BYTES * 256; // Size for 256 biomes value
|
|
||||||
for (int i = 0; i < chunkSections.length; i++) {
|
|
||||||
result += chunkSections[i].getSize();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeData(Buffer buffer) {
|
|
||||||
for (ChunkSection chunkSection : chunkSections) {
|
|
||||||
chunkSection.write(buffer);
|
|
||||||
}
|
|
||||||
// Biomes data
|
|
||||||
for (int i = 0; i < 256; i++) {
|
|
||||||
buffer.putInt(127); // Void biome
|
|
||||||
}
|
}
|
||||||
|
return blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return 0x21;
|
return 0x21;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ChunkSection {
|
|
||||||
|
|
||||||
public byte bitsPerBlock;
|
|
||||||
public int paletteLength; // Optional
|
|
||||||
public int[] palette; // Optional
|
|
||||||
public long[] data;
|
|
||||||
|
|
||||||
public void write(Buffer buffer) {
|
|
||||||
buffer.putShort((short) 3);
|
|
||||||
buffer.putByte(bitsPerBlock);
|
|
||||||
|
|
||||||
if (bitsPerBlock < 9) {
|
|
||||||
Utils.writeVarInt(buffer, paletteLength);
|
|
||||||
for (int p : palette) {
|
|
||||||
Utils.writeVarInt(buffer, p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Utils.writeVarInt(buffer, data.length);
|
|
||||||
for (long d : data) {
|
|
||||||
buffer.putLong(d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSize() {
|
|
||||||
int size = 0;
|
|
||||||
size += Short.BYTES;
|
|
||||||
size++; //bitsPerBlock
|
|
||||||
if (bitsPerBlock < 9) {
|
|
||||||
size += Utils.lengthVarInt(paletteLength);
|
|
||||||
for (int p : palette) {
|
|
||||||
size += Utils.lengthVarInt(p);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size += Utils.lengthVarInt(data.length);
|
|
||||||
size += Long.BYTES * data.length;
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,12 @@ public class EntityLookAndRelativeMovePacket implements ServerPacket {
|
|||||||
buffer.putShort(deltaX);
|
buffer.putShort(deltaX);
|
||||||
buffer.putShort(deltaY);
|
buffer.putShort(deltaY);
|
||||||
buffer.putShort(deltaZ);
|
buffer.putShort(deltaZ);
|
||||||
buffer.putFloat(yaw);
|
buffer.putByte((byte) (this.yaw * 256 / 360));
|
||||||
buffer.putFloat(pitch);
|
buffer.putByte((byte) (this.pitch * 256 / 360));
|
||||||
buffer.putBoolean(onGround);
|
buffer.putBoolean(onGround);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return 0x29;
|
return 0x29;
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
package fr.themode.minestom.utils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
public class GroupedCollections<E> implements Iterable<E> {
|
||||||
|
|
||||||
|
private ArrayList<Collection<E>> collections;
|
||||||
|
|
||||||
|
public GroupedCollections() {
|
||||||
|
this.collections = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return collections.stream().mapToInt(es -> es.size()).sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return collections.stream().allMatch(es -> es.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return collections.stream().anyMatch(es -> es.contains(o));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return collections.stream().flatMap(Collection::stream).iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*public Object[] toArray() {
|
||||||
|
return collections.stream().flatMap(Collection::stream).collect(Collectors.toList()).toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T[] toArray(T[] ts) {
|
||||||
|
return collections.stream().flatMap(Collection::stream).collect(Collectors.toList()).toArray(ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean containsAll(Collection<?> collection) {
|
||||||
|
return collections.stream().flatMap(Collection::stream).collect(Collectors.toList()).containsAll(collection);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
public void addCollection(Collection<E> list) {
|
||||||
|
this.collections.add(list);
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package fr.themode.minestom.utils;
|
package fr.themode.minestom.utils;
|
||||||
|
|
||||||
import fr.adamaq01.ozao.net.Buffer;
|
import fr.adamaq01.ozao.net.Buffer;
|
||||||
import fr.themode.minestom.world.CustomBlock;
|
import fr.themode.minestom.instance.Block;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -124,7 +124,7 @@ public class Utils {
|
|||||||
buffer.putLong(((x & 0x3FFFFFF) << 38) | ((y & 0xFFF) << 26) | (z & 0x3FFFFFF));
|
buffer.putLong(((x & 0x3FFFFFF) << 38) | ((y & 0xFFF) << 26) | (z & 0x3FFFFFF));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeBlocks(Buffer buffer, CustomBlock[] blocks, int bitsPerEntry) {
|
public static void writeBlocks(Buffer buffer, Block[] blocks, int bitsPerEntry) {
|
||||||
buffer.putShort((short) Arrays.stream(blocks).filter(customBlock -> customBlock.getType() != 0).collect(Collectors.toList()).size());
|
buffer.putShort((short) Arrays.stream(blocks).filter(customBlock -> customBlock.getType() != 0).collect(Collectors.toList()).size());
|
||||||
buffer.putByte((byte) bitsPerEntry);
|
buffer.putByte((byte) bitsPerEntry);
|
||||||
int[] blocksData = new int[16 * 16 * 16];
|
int[] blocksData = new int[16 * 16 * 16];
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
package fr.themode.minestom.world;
|
|
||||||
|
|
||||||
import fr.adamaq01.ozao.net.Buffer;
|
|
||||||
import fr.adamaq01.ozao.net.packet.Packet;
|
|
||||||
import fr.themode.minestom.utils.Utils;
|
|
||||||
import net.querz.nbt.CompoundTag;
|
|
||||||
import net.querz.nbt.LongArrayTag;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
public class ChunkPacketCreator {
|
|
||||||
|
|
||||||
public static Packet create(int chunkX, int chunkZ, CustomChunk customChunk, int start, int end) {
|
|
||||||
Packet packet = Packet.create();
|
|
||||||
packet.put("id", 0x21);
|
|
||||||
Buffer payload = packet.getPayload();
|
|
||||||
|
|
||||||
payload.putInt(chunkX);
|
|
||||||
payload.putInt(chunkZ);
|
|
||||||
payload.putBoolean(true); // Send biome data (loading chunk, not modifying it)
|
|
||||||
int mask = 0;
|
|
||||||
Buffer blocks = Buffer.create();
|
|
||||||
for (int i = 0; i < 16; i++) {
|
|
||||||
mask |= 1 << i;
|
|
||||||
CustomBlock[] section = getSection(customChunk, i);
|
|
||||||
Utils.writeBlocks(blocks, section, 14);
|
|
||||||
}
|
|
||||||
// Biome data
|
|
||||||
int[] biomeData = new int[256];
|
|
||||||
for (int z = 0; z < 16; z++) {
|
|
||||||
for (int x = 0; x < 16; x++) {
|
|
||||||
biomeData[z * 16 | x] = customChunk.getBiome().getId();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i < biomeData.length; i++) {
|
|
||||||
blocks.putInt(biomeData[i]);
|
|
||||||
}
|
|
||||||
Utils.writeVarInt(payload, mask);
|
|
||||||
|
|
||||||
// Heightmap
|
|
||||||
int[] motionBlocking = new int[16 * 16];
|
|
||||||
int[] worldSurface = new int[16 * 16];
|
|
||||||
for (int x = 0; x < 16; x++) {
|
|
||||||
for (int z = 0; z < 16; z++) {
|
|
||||||
motionBlocking[x + z * 16] = 4;
|
|
||||||
worldSurface[x + z * 16] = 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CompoundTag compound = new CompoundTag();
|
|
||||||
compound.put("MOTION_BLOCKING", new LongArrayTag(Utils.encodeBlocks(motionBlocking, 9)));
|
|
||||||
compound.put("WORLD_SURFACE", new LongArrayTag(Utils.encodeBlocks(worldSurface, 9)));
|
|
||||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
|
||||||
try {
|
|
||||||
compound.serialize(new DataOutputStream(outputStream), 100);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
byte[] data = outputStream.toByteArray();
|
|
||||||
payload.putBytes(data);
|
|
||||||
|
|
||||||
Utils.writeVarInt(payload, blocks.length());
|
|
||||||
payload.putBuffer(blocks);
|
|
||||||
Utils.writeVarInt(payload, 0);
|
|
||||||
|
|
||||||
return packet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CustomBlock[] getSection(CustomChunk customChunk, int section) {
|
|
||||||
CustomBlock[] blocks = new CustomBlock[16 * 16 * 16];
|
|
||||||
for (int y = 0; y < 16; y++) {
|
|
||||||
for (int x = 0; x < 16; x++) {
|
|
||||||
for (int z = 0; z < 16; z++) {
|
|
||||||
int index = (((y * 16) + x) * 16) + z;
|
|
||||||
blocks[index] = customChunk.getBlock(x, y + 16 * section, z);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return blocks;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
package fr.themode.minestom.world;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
public class CustomChunk {
|
|
||||||
|
|
||||||
private CustomBiome biome;
|
|
||||||
private HashMap<Short, CustomBlock> blocks;
|
|
||||||
|
|
||||||
public CustomChunk(CustomBiome biome) {
|
|
||||||
this.biome = biome;
|
|
||||||
this.blocks = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CustomBiome getBiome() {
|
|
||||||
return biome;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashMap<Short, CustomBlock> getBlocks() {
|
|
||||||
return blocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBlock(int x, int y, int z, CustomBlock customBlock) {
|
|
||||||
short index = (short) (x & 0x000F);
|
|
||||||
index |= (y << 4) & 0x0FF0;
|
|
||||||
index |= (z << 12) & 0xF000;
|
|
||||||
this.blocks.put(index, customBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CustomBlock getBlock(int x, int y, int z) {
|
|
||||||
short index = (short) (x & 0x000F);
|
|
||||||
index |= (y << 4) & 0x0FF0;
|
|
||||||
index |= (z << 12) & 0xF000;
|
|
||||||
return this.blocks.getOrDefault(index, new CustomBlock(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.format("CustomChunk{biome=%s, blocks=%s}", biome, blocks.values());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
package fr.themode.minestom.world;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
public class CustomWorld {
|
|
||||||
|
|
||||||
private String name;
|
|
||||||
private HashMap<Integer, CustomChunk> chunks;
|
|
||||||
|
|
||||||
public CustomWorld(String name) {
|
|
||||||
this.name = name;
|
|
||||||
this.chunks = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashMap<Integer, CustomChunk> getChunks() {
|
|
||||||
return chunks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChunk(int x, int z, CustomChunk customChunk) {
|
|
||||||
int index = x & 0x0000FFFF;
|
|
||||||
index |= (z << 16) & 0xFFFF0000;
|
|
||||||
this.chunks.put(index, customChunk);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CustomChunk getChunk(int x, int z) {
|
|
||||||
int index = x & 0x0000FFFF;
|
|
||||||
index |= (z << 16) & 0xFFFF0000;
|
|
||||||
return this.chunks.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBlock(int x, int y, int z, CustomBlock customBlock) {
|
|
||||||
int chunkX = x / 16;
|
|
||||||
int chunkZ = z / 16;
|
|
||||||
getChunk(chunkX, chunkZ).setBlock(x % 16, y, z % 16, customBlock);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CustomBlock getBlock(int x, int y, int z) {
|
|
||||||
int chunkX = x / 16;
|
|
||||||
int chunkZ = z / 16;
|
|
||||||
return getChunk(chunkX, chunkZ).getBlock(x % 16, y, z % 16);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.format("CustomWorld{name=%s, chunks=%s}", name, chunks.values());
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user