mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-13 19:51:27 +01:00
Auto chunk load & death manager
This commit is contained in:
parent
fc6f6afccc
commit
e0eb35e81e
@ -31,6 +31,10 @@ public class Main {
|
||||
public static final int TICK_MS = 50;
|
||||
public static final int TICK_PER_SECOND = 1000 / TICK_MS;
|
||||
|
||||
// Config
|
||||
public static final int CHUNK_VIEW_DISTANCE = 10;
|
||||
public static final int ENTITY_VIEW_DISTANCE = 10; // TODO
|
||||
|
||||
// Networking
|
||||
private static ConnectionManager connectionManager;
|
||||
private static PacketProcessor packetProcessor;
|
||||
|
@ -16,6 +16,10 @@ public class Data {
|
||||
return (T) data.get(key);
|
||||
}
|
||||
|
||||
// TODO serialize
|
||||
public <T> T getOrDefault(String key, T defaultValue) {
|
||||
return (T) data.getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
// TODO serialization
|
||||
|
||||
}
|
||||
|
@ -38,12 +38,14 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
protected Instance instance;
|
||||
protected Position position;
|
||||
protected boolean onGround;
|
||||
protected double lastX, lastY, lastZ;
|
||||
protected float lastX, lastY, lastZ;
|
||||
protected float lastYaw, lastPitch;
|
||||
private int id;
|
||||
|
||||
protected Entity vehicle;
|
||||
private Map<Class<Event>, Callback> eventCallbacks = new ConcurrentHashMap<>();
|
||||
// Velocity
|
||||
// TODO gravity implementation for entity other than players
|
||||
protected Vector velocity = new Vector(); // Movement in block per second
|
||||
private Set<Player> viewers = new CopyOnWriteArraySet<>();
|
||||
private Data data;
|
||||
private Set<Entity> passengers = new CopyOnWriteArraySet<>();
|
||||
@ -54,10 +56,7 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
private long scheduledRemoveTime;
|
||||
private int entityType;
|
||||
private long lastUpdate;
|
||||
|
||||
// Velocity
|
||||
// TODO gravity implementation for entity other than players
|
||||
protected float velocityX, velocityY, velocityZ; // Movement in block per second
|
||||
private Map<Class<? extends Event>, Callback> eventCallbacks = new ConcurrentHashMap<>();
|
||||
protected long velocityTime; // Reset velocity to 0 after countdown
|
||||
|
||||
// Synchronization
|
||||
@ -106,22 +105,45 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
// Called when entity a new instance is set
|
||||
public abstract void spawn();
|
||||
|
||||
public void teleport(Position position) {
|
||||
if (isChunkUnloaded(position.getX(), position.getZ()))
|
||||
public void teleport(Position position, Runnable callback) {
|
||||
if (instance == null)
|
||||
return;
|
||||
|
||||
refreshPosition(position.getX(), position.getY(), position.getZ());
|
||||
refreshView(position.getYaw(), position.getPitch());
|
||||
EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket();
|
||||
entityTeleportPacket.entityId = getEntityId();
|
||||
entityTeleportPacket.position = position;
|
||||
entityTeleportPacket.onGround = onGround;
|
||||
sendPacketToViewers(entityTeleportPacket);
|
||||
if (instance.hasEnabledAutoChunkLoad()) {
|
||||
instance.loadChunk(position, chunk -> {
|
||||
refreshPosition(position.getX(), position.getY(), position.getZ());
|
||||
refreshView(position.getYaw(), position.getPitch());
|
||||
EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket();
|
||||
entityTeleportPacket.entityId = getEntityId();
|
||||
entityTeleportPacket.position = position;
|
||||
entityTeleportPacket.onGround = onGround;
|
||||
sendPacketToViewers(entityTeleportPacket);
|
||||
if (callback != null)
|
||||
callback.run();
|
||||
});
|
||||
} else {
|
||||
if (isChunkUnloaded(position.getX(), position.getZ()))
|
||||
return;
|
||||
refreshPosition(position.getX(), position.getY(), position.getZ());
|
||||
refreshView(position.getYaw(), position.getPitch());
|
||||
EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket();
|
||||
entityTeleportPacket.entityId = getEntityId();
|
||||
entityTeleportPacket.position = position;
|
||||
entityTeleportPacket.onGround = onGround;
|
||||
sendPacketToViewers(entityTeleportPacket);
|
||||
if (callback != null)
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
|
||||
public void teleport(Position position) {
|
||||
teleport(position, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addViewer(Player player) {
|
||||
this.viewers.add(player);
|
||||
player.viewableEntity.add(this);
|
||||
player.getPlayerConnection().sendPacket(getVelocityPacket());
|
||||
}
|
||||
|
||||
@ -135,6 +157,7 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
destroyEntitiesPacket.entityIds = new int[]{getEntityId()};
|
||||
player.getPlayerConnection().sendPacket(destroyEntitiesPacket);
|
||||
}
|
||||
player.viewableEntity.remove(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -180,7 +203,7 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
sendPacketToViewersAndSelf(getVelocityPacket());
|
||||
} else {
|
||||
float tps = Main.TICK_PER_SECOND;
|
||||
refreshPosition(position.getX() + velocityX / tps, position.getY() + velocityY / tps, position.getZ() + velocityZ / tps);
|
||||
refreshPosition(position.getX() + velocity.getX() / tps, position.getY() + velocity.getY() / tps, position.getZ() + velocity.getZ() / tps);
|
||||
if (this instanceof ObjectEntity) {
|
||||
sendPacketToViewers(getVelocityPacket());
|
||||
} else if (this instanceof EntityCreature) {
|
||||
@ -207,11 +230,11 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
}
|
||||
|
||||
public <E extends Event> void setEventCallback(Class<E> eventClass, Callback<E> callback) {
|
||||
this.eventCallbacks.put((Class<Event>) eventClass, callback);
|
||||
this.eventCallbacks.put(eventClass, callback);
|
||||
}
|
||||
|
||||
public <E extends Event> Callback<E> getEventCallback(Class<E> eventClass) {
|
||||
return this.eventCallbacks.getOrDefault(eventClass, null);
|
||||
return this.eventCallbacks.get(eventClass);
|
||||
}
|
||||
|
||||
public <E extends Event> void callEvent(Class<E> eventClass, E event) {
|
||||
@ -261,17 +284,17 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
spawn();
|
||||
}
|
||||
|
||||
public Vector getVelocity() {
|
||||
return velocity;
|
||||
}
|
||||
|
||||
public void setVelocity(Vector velocity, long velocityTime) {
|
||||
this.velocityX = velocity.getX();
|
||||
this.velocityY = velocity.getY();
|
||||
this.velocityZ = velocity.getZ();
|
||||
this.velocity = velocity;
|
||||
this.velocityTime = System.currentTimeMillis() + velocityTime;
|
||||
}
|
||||
|
||||
public void resetVelocity() {
|
||||
this.velocityX = 0;
|
||||
this.velocityY = 0;
|
||||
this.velocityZ = 0;
|
||||
this.velocity = new Vector();
|
||||
this.velocityTime = 0;
|
||||
}
|
||||
|
||||
@ -341,6 +364,9 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
synchronized (instance) {
|
||||
instance.removeEntityFromChunk(this, lastChunk);
|
||||
instance.addEntityToChunk(this, newChunk);
|
||||
if (this instanceof Player)
|
||||
((Player) this).onChunkChange(lastChunk, newChunk); // Refresh loaded chunk
|
||||
// TODO compare with viewers and remove if too far away
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -388,9 +414,9 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
final float strength = 8000f / Main.TICK_PER_SECOND;
|
||||
EntityVelocityPacket velocityPacket = new EntityVelocityPacket();
|
||||
velocityPacket.entityId = getEntityId();
|
||||
velocityPacket.velocityX = (short) (velocityX * strength);
|
||||
velocityPacket.velocityY = (short) (velocityY * strength);
|
||||
velocityPacket.velocityZ = (short) (velocityZ * strength);
|
||||
velocityPacket.velocityX = (short) (velocity.getX() * strength);
|
||||
velocityPacket.velocityY = (short) (velocity.getY() * strength);
|
||||
velocityPacket.velocityZ = (short) (velocity.getZ() * strength);
|
||||
return velocityPacket;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
package fr.themode.minestom.entity;
|
||||
|
||||
import fr.themode.minestom.event.DeathEvent;
|
||||
import fr.themode.minestom.net.packet.server.play.EntityPacket;
|
||||
import fr.themode.minestom.net.packet.server.play.EntityRelativeMovePacket;
|
||||
import fr.themode.minestom.net.packet.server.play.SpawnMobPacket;
|
||||
@ -19,11 +20,6 @@ public abstract class EntityCreature extends LivingEntity {
|
||||
}
|
||||
|
||||
public void move(float x, float y, float z) {
|
||||
if (velocityTime != 0) {
|
||||
// Cancel movements if velocity is active
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO change yaw/pitch based on next position?
|
||||
Position position = getPosition();
|
||||
float newX = position.getX() + x;
|
||||
@ -49,6 +45,8 @@ public abstract class EntityCreature extends LivingEntity {
|
||||
this.isDead = true;
|
||||
triggerStatus((byte) 3);
|
||||
scheduleRemove(1000);
|
||||
DeathEvent deathEvent = new DeathEvent();
|
||||
callEvent(DeathEvent.class, deathEvent);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2,15 +2,18 @@ package fr.themode.minestom.entity;
|
||||
|
||||
import fr.themode.minestom.Main;
|
||||
import fr.themode.minestom.event.PlayerLoginEvent;
|
||||
import fr.themode.minestom.event.PlayerSpawnPacket;
|
||||
import fr.themode.minestom.event.PlayerSpawnEvent;
|
||||
import fr.themode.minestom.instance.Chunk;
|
||||
import fr.themode.minestom.instance.Instance;
|
||||
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.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class EntityManager {
|
||||
|
||||
@ -24,7 +27,7 @@ public class EntityManager {
|
||||
public void update() {
|
||||
waitingPlayersTick();
|
||||
for (Instance instance : instanceManager.getInstances()) {
|
||||
testTick1(instance);
|
||||
testTick2(instance);
|
||||
}
|
||||
|
||||
}
|
||||
@ -37,13 +40,24 @@ public class EntityManager {
|
||||
PlayerLoginEvent loginEvent = new PlayerLoginEvent();
|
||||
playerCache.callEvent(PlayerLoginEvent.class, loginEvent);
|
||||
Instance spawningInstance = loginEvent.getSpawningInstance() == null ? instanceManager.createInstanceContainer() : loginEvent.getSpawningInstance();
|
||||
// TODO load multiple chunks around player (based on view distance) instead of only one
|
||||
spawningInstance.loadChunk(playerCache.getPosition(), chunk -> {
|
||||
playerCache.spawned = true;
|
||||
playerCache.setInstance(spawningInstance);
|
||||
PlayerSpawnPacket spawnPacket = new PlayerSpawnPacket();
|
||||
playerCache.callEvent(PlayerSpawnPacket.class, spawnPacket);
|
||||
});
|
||||
Position position = playerCache.getPosition();
|
||||
|
||||
long[] visibleChunks = ChunkUtils.getVisibleChunks(position);
|
||||
for (int i = 0; i < visibleChunks.length; i++) {
|
||||
int[] chunkPos = ChunkUtils.getChunkCoord(visibleChunks[i]);
|
||||
int chunkX = chunkPos[0];
|
||||
int chunkZ = chunkPos[1];
|
||||
boolean isLast = i == visibleChunks.length - 1;
|
||||
Consumer<Chunk> callback = isLast ? chunk -> {
|
||||
System.out.println("END CHUNK LOADING");
|
||||
playerCache.spawned = true;
|
||||
playerCache.setInstance(spawningInstance);
|
||||
PlayerSpawnEvent spawnEvent = new PlayerSpawnEvent();
|
||||
playerCache.callEvent(PlayerSpawnEvent.class, spawnEvent);
|
||||
} : null;
|
||||
spawningInstance.loadChunk(chunkX, chunkZ, callback); // TODO loadOptionalChunk for not loading chunks when autoload is false
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package fr.themode.minestom.entity;
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.item.ItemStack;
|
||||
import fr.themode.minestom.utils.Utils;
|
||||
import fr.themode.minestom.utils.Vector;
|
||||
|
||||
public class ItemEntity extends ObjectEntity {
|
||||
|
||||
@ -22,7 +21,7 @@ public class ItemEntity extends ObjectEntity {
|
||||
|
||||
@Override
|
||||
public void spawn() {
|
||||
setVelocity(new Vector(0, 1, 0), 5000);
|
||||
// setVelocity(new Vector(0, 1, 0), 5000);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -3,8 +3,11 @@ package fr.themode.minestom.entity;
|
||||
import fr.themode.minestom.Main;
|
||||
import fr.themode.minestom.bossbar.BossBar;
|
||||
import fr.themode.minestom.chat.Chat;
|
||||
import fr.themode.minestom.data.Data;
|
||||
import fr.themode.minestom.data.DataType;
|
||||
import fr.themode.minestom.entity.demo.ChickenCreature;
|
||||
import fr.themode.minestom.event.*;
|
||||
import fr.themode.minestom.instance.Chunk;
|
||||
import fr.themode.minestom.instance.CustomBlock;
|
||||
import fr.themode.minestom.instance.Instance;
|
||||
import fr.themode.minestom.instance.InstanceContainer;
|
||||
@ -17,6 +20,7 @@ import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||
import fr.themode.minestom.net.packet.server.play.*;
|
||||
import fr.themode.minestom.net.player.PlayerConnection;
|
||||
import fr.themode.minestom.utils.BlockPosition;
|
||||
import fr.themode.minestom.utils.ChunkUtils;
|
||||
import fr.themode.minestom.utils.Position;
|
||||
import fr.themode.minestom.utils.Vector;
|
||||
import fr.themode.minestom.world.Dimension;
|
||||
@ -41,10 +45,30 @@ public class Player extends LivingEntity {
|
||||
private GameMode gameMode;
|
||||
private LevelType levelType;
|
||||
|
||||
static {
|
||||
ChunkGeneratorDemo chunkGeneratorDemo = new ChunkGeneratorDemo();
|
||||
//instance = Main.getInstanceManager().createInstance(new File("C:\\Users\\themo\\OneDrive\\Bureau\\Minestom data"));
|
||||
instanceContainer = Main.getInstanceManager().createInstanceContainer();
|
||||
instanceContainer.enableAutoChunkLoad(true);
|
||||
instanceContainer.setChunkGenerator(chunkGeneratorDemo);
|
||||
int loopStart = -3;
|
||||
int loopEnd = 3;
|
||||
long time = System.currentTimeMillis();
|
||||
for (int x = loopStart; x < loopEnd; x++)
|
||||
for (int z = loopStart; z < loopEnd; z++) {
|
||||
instanceContainer.loadChunk(x, z);
|
||||
}
|
||||
System.out.println("Time to load all chunks: " + (System.currentTimeMillis() - time) + " ms");
|
||||
}
|
||||
|
||||
protected Set<Entity> viewableEntity = new CopyOnWriteArraySet<>();
|
||||
private float health;
|
||||
|
||||
private PlayerSettings settings;
|
||||
private PlayerInventory inventory;
|
||||
private short heldSlot;
|
||||
private Inventory openInventory;
|
||||
private int food;
|
||||
|
||||
private CustomBlock targetCustomBlock;
|
||||
private BlockPosition targetBlockPosition;
|
||||
@ -58,21 +82,7 @@ public class Player extends LivingEntity {
|
||||
private float forward;
|
||||
|
||||
private static InstanceContainer instanceContainer;
|
||||
|
||||
static {
|
||||
ChunkGeneratorDemo chunkGeneratorDemo = new ChunkGeneratorDemo();
|
||||
//instance = Main.getInstanceManager().createInstance(new File("C:\\Users\\themo\\OneDrive\\Bureau\\Minestom data"));
|
||||
instanceContainer = Main.getInstanceManager().createInstanceContainer();
|
||||
instanceContainer.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++) {
|
||||
instanceContainer.loadChunk(x, z);
|
||||
}
|
||||
System.out.println("Time to load all chunks: " + (System.currentTimeMillis() - time) + " ms");
|
||||
}
|
||||
private float foodSaturation;
|
||||
|
||||
protected boolean spawned;
|
||||
|
||||
@ -82,6 +92,8 @@ public class Player extends LivingEntity {
|
||||
this.username = username;
|
||||
this.playerConnection = playerConnection;
|
||||
|
||||
refreshHealth();
|
||||
|
||||
this.settings = new PlayerSettings();
|
||||
this.inventory = new PlayerInventory(this);
|
||||
|
||||
@ -97,28 +109,17 @@ public class Player extends LivingEntity {
|
||||
Vector velocity = getPosition().clone().getDirection().multiply(6);
|
||||
velocity.setY(3.5f);
|
||||
player.setVelocity(velocity, 150);
|
||||
|
||||
AnimationPacket animationPacket = new AnimationPacket();
|
||||
animationPacket.entityId = player.getEntityId();
|
||||
animationPacket.animation = AnimationPacket.Animation.TAKE_DAMAGE;
|
||||
sendPacketToViewersAndSelf(animationPacket);
|
||||
|
||||
player.damage(2);
|
||||
sendMessage("ATTACK");
|
||||
}
|
||||
/*UpdateHealthPacket updateHealthPacket = new UpdateHealthPacket();
|
||||
updateHealthPacket.health = -1f;
|
||||
updateHealthPacket.food = 5;
|
||||
updateHealthPacket.foodSaturation = 0;
|
||||
playerConnection.sendPacket(updateHealthPacket);*/
|
||||
});
|
||||
|
||||
setEventCallback(BlockPlaceEvent.class, event -> {
|
||||
/*sendMessage("Placed block! " + event.getHand());
|
||||
Data data = new Data();
|
||||
data.set("test", 5, DataType.INTEGER);
|
||||
setData(data);
|
||||
setEventCallback(PlayerBlockPlaceEvent.class, event -> {
|
||||
sendMessage("Placed block! " + event.getHand());
|
||||
int value = getData().getOrDefault("test", 0);
|
||||
getData().set("test", value + 1, DataType.INTEGER);
|
||||
|
||||
sendMessage("Data: " + getData().get("test"));*/
|
||||
System.out.println("OLD DATA VALUE: " + value);
|
||||
if (event.getHand() != Hand.MAIN)
|
||||
return;
|
||||
|
||||
@ -128,9 +129,10 @@ public class Player extends LivingEntity {
|
||||
sendMessage("Saved in " + (System.currentTimeMillis() - time) + " ms");
|
||||
});*/
|
||||
|
||||
for (Player player : Main.getConnectionManager().getOnlinePlayers()) {
|
||||
player.teleport(getPosition());
|
||||
}
|
||||
/*for (Player player : instance.getPlayers()) {
|
||||
if (player != this)
|
||||
player.teleport(getPosition());
|
||||
}*/
|
||||
});
|
||||
|
||||
setEventCallback(PickupItemEvent.class, event -> {
|
||||
@ -139,19 +141,17 @@ public class Player extends LivingEntity {
|
||||
|
||||
setEventCallback(PlayerLoginEvent.class, event -> {
|
||||
event.setSpawningInstance(instanceContainer);
|
||||
setData(new Data());
|
||||
});
|
||||
|
||||
setEventCallback(PlayerSpawnPacket.class, event -> {
|
||||
setGameMode(GameMode.SURVIVAL);
|
||||
setEventCallback(PlayerSpawnEvent.class, event -> {
|
||||
System.out.println("SPAWN ");
|
||||
setGameMode(GameMode.CREATIVE);
|
||||
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(getInstance());
|
||||
//chickenCreature.addPassenger(player);
|
||||
}
|
||||
|
||||
ChickenCreature chickenCreature = new ChickenCreature();
|
||||
chickenCreature.refreshPosition(2, 65, 2);
|
||||
chickenCreature.setInstance(getInstance());
|
||||
|
||||
for (int ix = 0; ix < 4; ix++)
|
||||
for (int iz = 0; iz < 4; iz++) {
|
||||
@ -199,7 +199,7 @@ public class Player extends LivingEntity {
|
||||
}
|
||||
this.targetLastStage = stage;
|
||||
if (stage > 9) {
|
||||
instance.breakBlock(this, targetBlockPosition, targetCustomBlock);
|
||||
instance.breakBlock(this, targetBlockPosition);
|
||||
resetTargetBlock();
|
||||
}
|
||||
}
|
||||
@ -271,6 +271,15 @@ public class Player extends LivingEntity {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
clearBossBars();
|
||||
if (getOpenInventory() != null)
|
||||
getOpenInventory().removeViewer(this);
|
||||
this.viewableEntity.forEach(entity -> entity.removeViewer(this));
|
||||
super.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addViewer(Player player) {
|
||||
super.addViewer(player);
|
||||
@ -315,7 +324,7 @@ public class Player extends LivingEntity {
|
||||
@Override
|
||||
public void kill() {
|
||||
this.isDead = true;
|
||||
// TODO set health to -1
|
||||
setHealth(-1);
|
||||
}
|
||||
|
||||
public void sendBlockBreakAnimation(BlockPosition blockPosition, byte destroyStage) {
|
||||
@ -331,19 +340,165 @@ public class Player extends LivingEntity {
|
||||
playerConnection.sendPacket(chatMessagePacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleport(Position position) {
|
||||
super.teleport(position); // Send new position to all viewers directly
|
||||
if (isChunkUnloaded(position.getX(), position.getZ()))
|
||||
public void damage(float amount) {
|
||||
AnimationPacket animationPacket = new AnimationPacket();
|
||||
animationPacket.entityId = getEntityId();
|
||||
animationPacket.animation = AnimationPacket.Animation.TAKE_DAMAGE;
|
||||
sendPacketToViewersAndSelf(animationPacket);
|
||||
setHealth(health - amount);
|
||||
}
|
||||
|
||||
public float getHealth() {
|
||||
return health;
|
||||
}
|
||||
|
||||
public void setHealth(float health) {
|
||||
this.health = health;
|
||||
sendUpdateHealthPacket();
|
||||
if (this.health <= 0) {
|
||||
// Kill player
|
||||
refreshIsDead(true);
|
||||
EntityStatusPacket entityStatusPacket = new EntityStatusPacket();
|
||||
entityStatusPacket.entityId = getEntityId();
|
||||
entityStatusPacket.status = 3; // Death sound/animation
|
||||
sendPacketToViewers(entityStatusPacket);
|
||||
DeathEvent deathEvent = new DeathEvent();
|
||||
callEvent(DeathEvent.class, deathEvent);
|
||||
}
|
||||
}
|
||||
|
||||
public int getFood() {
|
||||
return food;
|
||||
}
|
||||
|
||||
public void setFood(int food) {
|
||||
this.food = food;
|
||||
sendUpdateHealthPacket();
|
||||
}
|
||||
|
||||
public float getFoodSaturation() {
|
||||
return foodSaturation;
|
||||
}
|
||||
|
||||
public void setFoodSaturation(float foodSaturation) {
|
||||
this.foodSaturation = foodSaturation;
|
||||
sendUpdateHealthPacket();
|
||||
}
|
||||
|
||||
public void respawn() {
|
||||
if (!isDead())
|
||||
return;
|
||||
|
||||
refreshPosition(position.getX(), position.getY(), position.getZ());
|
||||
refreshView(position.getYaw(), position.getPitch());
|
||||
PlayerPositionAndLookPacket positionAndLookPacket = new PlayerPositionAndLookPacket();
|
||||
positionAndLookPacket.position = position;
|
||||
positionAndLookPacket.flags = 0x00;
|
||||
positionAndLookPacket.teleportId = 67;
|
||||
playerConnection.sendPacket(positionAndLookPacket);
|
||||
refreshHealth();
|
||||
RespawnPacket respawnPacket = new RespawnPacket();
|
||||
respawnPacket.dimension = getDimension();
|
||||
respawnPacket.gameMode = getGameMode();
|
||||
respawnPacket.levelType = getLevelType();
|
||||
getPlayerConnection().sendPacket(respawnPacket);
|
||||
PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(getPosition());
|
||||
callEvent(PlayerRespawnEvent.class, respawnEvent);
|
||||
refreshIsDead(false);
|
||||
|
||||
// Runnable called when teleportation is successfull (after loading and sending necessary chunk)
|
||||
teleport(respawnEvent.getRespawnPosition(), () -> {
|
||||
getInventory().update();
|
||||
|
||||
SpawnPlayerPacket spawnPlayerPacket = new SpawnPlayerPacket();
|
||||
spawnPlayerPacket.entityId = getEntityId();
|
||||
spawnPlayerPacket.playerUuid = getUuid();
|
||||
spawnPlayerPacket.position = getPosition();
|
||||
sendPacketToViewers(spawnPlayerPacket);
|
||||
});
|
||||
}
|
||||
|
||||
private void refreshHealth() {
|
||||
// TODO improve
|
||||
this.health = 20;
|
||||
this.food = 20;
|
||||
this.foodSaturation = 5;
|
||||
}
|
||||
|
||||
protected void sendUpdateHealthPacket() {
|
||||
UpdateHealthPacket updateHealthPacket = new UpdateHealthPacket();
|
||||
updateHealthPacket.health = health;
|
||||
updateHealthPacket.food = food;
|
||||
updateHealthPacket.foodSaturation = foodSaturation;
|
||||
playerConnection.sendPacket(updateHealthPacket);
|
||||
}
|
||||
|
||||
protected void onChunkChange(Chunk lastChunk, Chunk newChunk) {
|
||||
long[] lastVisibleChunks = ChunkUtils.getVisibleChunks(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()));
|
||||
long[] newVisibleChunks = ChunkUtils.getVisibleChunks(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()));
|
||||
|
||||
// Unload old chunks
|
||||
for (int l = 0; l < lastVisibleChunks.length; l++) {
|
||||
long lastVisibleChunk = lastVisibleChunks[l];
|
||||
boolean contains = false;
|
||||
for (int n = 0; n < newVisibleChunks.length; n++) {
|
||||
long newVisibleChunk = newVisibleChunks[n];
|
||||
if (newVisibleChunk == lastVisibleChunk) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!contains) {
|
||||
int[] chunkPos = ChunkUtils.getChunkCoord(lastVisibleChunk);
|
||||
UnloadChunkPacket unloadChunkPacket = new UnloadChunkPacket();
|
||||
unloadChunkPacket.chunkX = chunkPos[0];
|
||||
unloadChunkPacket.chunkZ = chunkPos[1];
|
||||
playerConnection.sendPacket(unloadChunkPacket);
|
||||
}
|
||||
}
|
||||
|
||||
// Load new chunks
|
||||
for (int n = 0; n < newVisibleChunks.length; n++) {
|
||||
long newVisibleChunk = newVisibleChunks[n];
|
||||
boolean contains = false;
|
||||
for (int l = 0; l < lastVisibleChunks.length; l++) {
|
||||
long lastVisibleChunk = lastVisibleChunks[l];
|
||||
if (lastVisibleChunk == newVisibleChunk) {
|
||||
contains = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!contains) {
|
||||
int[] chunkPos = ChunkUtils.getChunkCoord(newVisibleChunk);
|
||||
instance.loadOptionalChunk(chunkPos[0], chunkPos[1], chunk -> {
|
||||
if (chunk == null) {
|
||||
return; // Cannot load chunk (autoload not enabled)
|
||||
}
|
||||
instance.sendChunk(this, chunk);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
UpdateViewPositionPacket updateViewPositionPacket = new UpdateViewPositionPacket(newChunk);
|
||||
playerConnection.sendPacket(updateViewPositionPacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleport(Position position, Runnable callback) {
|
||||
if (instance == null)
|
||||
return;
|
||||
super.teleport(position, () -> {
|
||||
if (!instance.hasEnabledAutoChunkLoad() && isChunkUnloaded(position.getX(), position.getZ()))
|
||||
return;
|
||||
refreshPosition(position.getX(), position.getY(), position.getZ());
|
||||
refreshView(position.getYaw(), position.getPitch());
|
||||
PlayerPositionAndLookPacket positionAndLookPacket = new PlayerPositionAndLookPacket();
|
||||
positionAndLookPacket.position = position;
|
||||
positionAndLookPacket.flags = 0x00;
|
||||
positionAndLookPacket.teleportId = 67;
|
||||
playerConnection.sendPacket(positionAndLookPacket);
|
||||
if (callback != null)
|
||||
callback.run();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleport(Position position) {
|
||||
teleport(position, null);
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
@ -465,6 +620,10 @@ public class Player extends LivingEntity {
|
||||
inventory.update();
|
||||
}
|
||||
|
||||
public void clearBossBars() {
|
||||
this.bossBars.forEach(bossBar -> bossBar.removeViewer(this));
|
||||
}
|
||||
|
||||
public void syncEquipment(EntityEquipmentPacket.Slot slot) {
|
||||
EntityEquipmentPacket equipmentPacket = new EntityEquipmentPacket();
|
||||
equipmentPacket.entityId = getEntityId();
|
||||
|
@ -1,6 +1,7 @@
|
||||
package fr.themode.minestom.entity.demo;
|
||||
|
||||
import fr.themode.minestom.entity.EntityCreature;
|
||||
import fr.themode.minestom.utils.Vector;
|
||||
|
||||
public class ChickenCreature extends EntityCreature {
|
||||
|
||||
@ -42,12 +43,11 @@ public class ChickenCreature extends EntityCreature {
|
||||
move(x * speed, 0, z * speed);
|
||||
}
|
||||
}*/
|
||||
|
||||
move(0, 0, speed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawn() {
|
||||
// setVelocity(new Vector(0, 1, 0), 1000);
|
||||
setVelocity(new Vector(0, 1, 0), 3000);
|
||||
}
|
||||
}
|
||||
|
7
src/main/java/fr/themode/minestom/event/DeathEvent.java
Normal file
7
src/main/java/fr/themode/minestom/event/DeathEvent.java
Normal file
@ -0,0 +1,7 @@
|
||||
package fr.themode.minestom.event;
|
||||
|
||||
public class DeathEvent extends Event {
|
||||
|
||||
// TODO cause
|
||||
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
package fr.themode.minestom.event;
|
||||
|
||||
public class Event {
|
||||
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
package fr.themode.minestom.event;
|
||||
|
||||
import fr.themode.minestom.entity.Entity;
|
||||
|
||||
public class InteractEvent extends Event {
|
||||
|
||||
private Entity target;
|
||||
|
||||
public InteractEvent(Entity target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public Entity getTarget() {
|
||||
return target;
|
||||
}
|
||||
}
|
@ -2,11 +2,11 @@ package fr.themode.minestom.event;
|
||||
|
||||
import fr.themode.minestom.utils.BlockPosition;
|
||||
|
||||
public class BlockBreakEvent extends CancellableEvent {
|
||||
public class PlayerBlockBreakEvent extends CancellableEvent {
|
||||
|
||||
private BlockPosition blockPosition;
|
||||
|
||||
public BlockBreakEvent(BlockPosition blockPosition) {
|
||||
public PlayerBlockBreakEvent(BlockPosition blockPosition) {
|
||||
this.blockPosition = blockPosition;
|
||||
}
|
||||
|
@ -3,13 +3,13 @@ package fr.themode.minestom.event;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.utils.BlockPosition;
|
||||
|
||||
public class BlockPlaceEvent extends CancellableEvent {
|
||||
public class PlayerBlockPlaceEvent extends CancellableEvent {
|
||||
|
||||
private short blockId;
|
||||
private BlockPosition blockPosition;
|
||||
private Player.Hand hand;
|
||||
|
||||
public BlockPlaceEvent(short blockId, BlockPosition blockPosition, Player.Hand hand) {
|
||||
public PlayerBlockPlaceEvent(short blockId, BlockPosition blockPosition, Player.Hand hand) {
|
||||
this.blockId = blockId;
|
||||
this.blockPosition = blockPosition;
|
||||
this.hand = hand;
|
@ -0,0 +1,23 @@
|
||||
package fr.themode.minestom.event;
|
||||
|
||||
import fr.themode.minestom.entity.Entity;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
|
||||
public class PlayerInteractEvent extends Event {
|
||||
|
||||
private Entity target;
|
||||
private Player.Hand hand;
|
||||
|
||||
public PlayerInteractEvent(Entity target, Player.Hand hand) {
|
||||
this.target = target;
|
||||
this.hand = hand;
|
||||
}
|
||||
|
||||
public Entity getTarget() {
|
||||
return target;
|
||||
}
|
||||
|
||||
public Player.Hand getHand() {
|
||||
return hand;
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package fr.themode.minestom.event;
|
||||
|
||||
public class PlayerSpawnEvent extends Event {
|
||||
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package fr.themode.minestom.event;
|
||||
|
||||
public class PlayerSpawnPacket extends Event {
|
||||
|
||||
}
|
@ -2,11 +2,11 @@ package fr.themode.minestom.event;
|
||||
|
||||
import fr.themode.minestom.instance.CustomBlock;
|
||||
|
||||
public class StartDiggingEvent extends CancellableEvent {
|
||||
public class PlayerStartDiggingEvent extends CancellableEvent {
|
||||
|
||||
private CustomBlock customBlock;
|
||||
|
||||
public StartDiggingEvent(CustomBlock customBlock) {
|
||||
public PlayerStartDiggingEvent(CustomBlock customBlock) {
|
||||
this.customBlock = customBlock;
|
||||
}
|
||||
|
@ -3,12 +3,12 @@ package fr.themode.minestom.event;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.item.ItemStack;
|
||||
|
||||
public class UseItemEvent extends Event {
|
||||
public class PlayerUseItemEvent extends Event {
|
||||
|
||||
private Player.Hand hand;
|
||||
private ItemStack itemStack;
|
||||
|
||||
public UseItemEvent(Player.Hand hand, ItemStack itemStack) {
|
||||
public PlayerUseItemEvent(Player.Hand hand, ItemStack itemStack) {
|
||||
this.hand = hand;
|
||||
this.itemStack = itemStack;
|
||||
}
|
@ -76,7 +76,7 @@ public class BlockBatch implements BlockModifier {
|
||||
if (blockIdentifier == null) {
|
||||
chunk.setBlock((byte) x, (byte) y, (byte) z, blockId);
|
||||
} else {
|
||||
chunk.setBlock((byte) x, (byte) y, (byte) z, blockIdentifier);
|
||||
chunk.setCustomBlock((byte) x, (byte) y, (byte) z, blockIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,10 +14,10 @@ import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
public class Chunk {
|
||||
|
||||
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;
|
||||
public static final int CHUNK_SIZE_X = 16;
|
||||
public static final int CHUNK_SIZE_Y = 256;
|
||||
public static final int CHUNK_SIZE_Z = 16;
|
||||
public static final int CHUNK_SIZE = CHUNK_SIZE_X * CHUNK_SIZE_Y * CHUNK_SIZE_Z;
|
||||
|
||||
private Biome biome;
|
||||
private int chunkX, chunkZ;
|
||||
@ -41,7 +41,7 @@ public class Chunk {
|
||||
setBlock(x, y, z, blockId, (short) 0);
|
||||
}
|
||||
|
||||
protected void setBlock(byte x, byte y, byte z, String blockId) {
|
||||
protected void setCustomBlock(byte x, byte y, byte z, String blockId) {
|
||||
CustomBlock customBlock = Main.getBlockManager().getBlock(blockId);
|
||||
if (customBlock == null)
|
||||
throw new IllegalArgumentException("The block " + blockId + " does not exist or isn't registered");
|
||||
@ -151,15 +151,27 @@ public class Chunk {
|
||||
|
||||
}
|
||||
|
||||
protected ChunkDataPacket getFreshFullDataPacket() {
|
||||
public ChunkDataPacket getFreshFullDataPacket() {
|
||||
ChunkDataPacket fullDataPacket = new ChunkDataPacket();
|
||||
fullDataPacket.chunk = this;
|
||||
fullDataPacket.fullChunk = true;
|
||||
return fullDataPacket;
|
||||
}
|
||||
|
||||
public ChunkDataPacket getFreshPartialDataPacket() {
|
||||
ChunkDataPacket fullDataPacket = new ChunkDataPacket();
|
||||
fullDataPacket.chunk = this;
|
||||
fullDataPacket.fullChunk = false;
|
||||
return fullDataPacket;
|
||||
}
|
||||
|
||||
protected void refreshDataPacket() {
|
||||
Packet packet = PacketUtils.writePacket(getFreshFullDataPacket());
|
||||
this.fullDataPacket = PacketUtils.encode(packet); // TODO write packet buffer in another thread (heavy calculations)
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Chunk[" + chunkX + ":" + chunkZ + "]";
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ public class ChunkBatch implements BlockModifier {
|
||||
for (BlockData data : dataList) {
|
||||
data.apply(chunk);
|
||||
}
|
||||
// System.out.println("FINISHED chunk creation " + chunk.getChunkX() + ":" + chunk.getChunkZ());
|
||||
chunk.refreshDataPacket(); // TODO partial refresh instead of full
|
||||
instance.sendChunkUpdate(chunk); // TODO partial chunk data
|
||||
if (callback != null)
|
||||
@ -68,7 +69,7 @@ public class ChunkBatch implements BlockModifier {
|
||||
if (blockIdentifier == null) {
|
||||
chunk.setBlock(x, y, z, blockId);
|
||||
} else {
|
||||
chunk.setBlock(x, y, z, blockIdentifier);
|
||||
chunk.setCustomBlock(x, y, z, blockIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,8 +5,8 @@ import fr.themode.minestom.entity.Entity;
|
||||
import fr.themode.minestom.entity.EntityCreature;
|
||||
import fr.themode.minestom.entity.ObjectEntity;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.net.packet.server.play.DestroyEntitiesPacket;
|
||||
import fr.themode.minestom.utils.BlockPosition;
|
||||
import fr.themode.minestom.utils.ChunkUtils;
|
||||
import fr.themode.minestom.utils.Position;
|
||||
|
||||
import java.io.File;
|
||||
@ -30,10 +30,13 @@ public abstract class Instance implements BlockModifier {
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
|
||||
public abstract void breakBlock(Player player, BlockPosition blockPosition, short blockId);
|
||||
public abstract void breakBlock(Player player, BlockPosition blockPosition);
|
||||
|
||||
public abstract void loadChunk(int chunkX, int chunkZ, Consumer<Chunk> callback);
|
||||
|
||||
// Load only if auto chunk load is enabled
|
||||
public abstract void loadOptionalChunk(int chunkX, int chunkZ, Consumer<Chunk> callback);
|
||||
|
||||
public abstract Chunk getChunk(int chunkX, int chunkZ);
|
||||
|
||||
public abstract void saveToFolder(Runnable callback);
|
||||
@ -52,12 +55,18 @@ public abstract class Instance implements BlockModifier {
|
||||
|
||||
public abstract void sendChunkUpdate(Player player, Chunk chunk);
|
||||
|
||||
public 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 sendChunks(Player player);
|
||||
|
||||
public abstract void sendChunk(Player player, Chunk chunk);
|
||||
|
||||
public abstract void enableAutoChunkLoad(boolean enable);
|
||||
|
||||
public abstract boolean hasEnabledAutoChunkLoad();
|
||||
|
||||
//
|
||||
protected void sendChunkUpdate(Collection<Player> players, Chunk chunk) {
|
||||
Buffer chunkData = chunk.getFullDataPacket();
|
||||
@ -82,11 +91,7 @@ public abstract class Instance implements BlockModifier {
|
||||
}
|
||||
|
||||
public Set<Entity> getChunkEntities(Chunk chunk) {
|
||||
return Collections.unmodifiableSet(getEntitiesInChunk(getChunkIndex(chunk.getChunkX(), chunk.getChunkZ())));
|
||||
}
|
||||
|
||||
public void breakBlock(Player player, BlockPosition blockPosition, CustomBlock customBlock) {
|
||||
breakBlock(player, blockPosition, customBlock.getType());
|
||||
return Collections.unmodifiableSet(getEntitiesInChunk(ChunkUtils.getChunkIndex(chunk.getChunkX(), chunk.getChunkZ())));
|
||||
}
|
||||
|
||||
public void loadChunk(int chunkX, int chunkZ) {
|
||||
@ -139,24 +144,21 @@ public abstract class Instance implements BlockModifier {
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
protected long getChunkIndex(int chunkX, int chunkZ) {
|
||||
return (((long) chunkX) << 32) | (chunkZ & 0xffffffffL);
|
||||
}
|
||||
|
||||
// UNSAFE METHODS (need most of time to be synchronized)
|
||||
|
||||
public void addEntity(Entity entity) {
|
||||
Instance lastInstance = entity.getInstance();
|
||||
if (lastInstance != null && lastInstance != this) {
|
||||
lastInstance.removeEntity(entity);
|
||||
lastInstance.removeEntity(entity); // If entity is in another instance, remove it from there and add it to this
|
||||
}
|
||||
|
||||
// TODO based on distance with players
|
||||
getPlayers().forEach(p -> entity.addViewer(p));
|
||||
getPlayers().forEach(p -> entity.addViewer(p)); // Add new entity to all players viewable list
|
||||
|
||||
if (entity instanceof Player) {
|
||||
Player player = (Player) entity;
|
||||
sendChunks(player);
|
||||
// Send player all current entity in the instance
|
||||
getObjectEntities().forEach(objectEntity -> objectEntity.addViewer(player));
|
||||
getCreatures().forEach(entityCreature -> entityCreature.addViewer(player));
|
||||
getPlayers().forEach(p -> p.addViewer(player));
|
||||
@ -171,28 +173,14 @@ public abstract class Instance implements BlockModifier {
|
||||
if (entityInstance == null || entityInstance != this)
|
||||
return;
|
||||
|
||||
entity.getViewers().forEach(p -> entity.removeViewer(p));
|
||||
|
||||
if (!(entity instanceof Player)) {
|
||||
DestroyEntitiesPacket destroyEntitiesPacket = new DestroyEntitiesPacket();
|
||||
destroyEntitiesPacket.entityIds = new int[]{entity.getEntityId()};
|
||||
|
||||
entity.getViewers().forEach(p -> p.getPlayerConnection().sendPacket(destroyEntitiesPacket)); // TODO destroy batch
|
||||
} else {
|
||||
// TODO optimize (cache all entities that the player see)
|
||||
Player player = (Player) entity;
|
||||
getObjectEntities().forEach(objectEntity -> objectEntity.removeViewer(player));
|
||||
getCreatures().forEach(entityCreature -> entityCreature.removeViewer(player));
|
||||
getPlayers().forEach(p -> p.removeViewer(player));
|
||||
|
||||
}
|
||||
entity.getViewers().forEach(p -> entity.removeViewer(p)); // Remove this entity from players viewable list and send delete entities packet
|
||||
|
||||
Chunk chunk = getChunkAt(entity.getPosition());
|
||||
removeEntityFromChunk(entity, chunk);
|
||||
}
|
||||
|
||||
public void addEntityToChunk(Entity entity, Chunk chunk) {
|
||||
long chunkIndex = getChunkIndex(chunk.getChunkX(), chunk.getChunkZ());
|
||||
long chunkIndex = ChunkUtils.getChunkIndex(chunk.getChunkX(), chunk.getChunkZ());
|
||||
Set<Entity> entities = getEntitiesInChunk(chunkIndex);
|
||||
entities.add(entity);
|
||||
this.chunkEntities.put(chunkIndex, entities);
|
||||
@ -206,10 +194,14 @@ public abstract class Instance implements BlockModifier {
|
||||
}
|
||||
|
||||
public void removeEntityFromChunk(Entity entity, Chunk chunk) {
|
||||
long chunkIndex = getChunkIndex(chunk.getChunkX(), chunk.getChunkZ());
|
||||
long chunkIndex = ChunkUtils.getChunkIndex(chunk.getChunkX(), chunk.getChunkZ());
|
||||
Set<Entity> entities = getEntitiesInChunk(chunkIndex);
|
||||
entities.remove(entity);
|
||||
this.chunkEntities.put(chunkIndex, entities);
|
||||
if (entities.isEmpty()) {
|
||||
this.chunkEntities.remove(chunkIndex);
|
||||
} else {
|
||||
this.chunkEntities.put(chunkIndex, entities);
|
||||
}
|
||||
if (entity instanceof Player) {
|
||||
this.players.remove(entity);
|
||||
} else if (entity instanceof EntityCreature) {
|
||||
|
@ -2,10 +2,11 @@ package fr.themode.minestom.instance;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.event.BlockBreakEvent;
|
||||
import fr.themode.minestom.event.PlayerBlockBreakEvent;
|
||||
import fr.themode.minestom.net.PacketWriter;
|
||||
import fr.themode.minestom.net.packet.server.play.ParticlePacket;
|
||||
import fr.themode.minestom.utils.BlockPosition;
|
||||
import fr.themode.minestom.utils.ChunkUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
@ -25,6 +26,8 @@ public class InstanceContainer extends Instance {
|
||||
private ChunkGenerator chunkGenerator;
|
||||
private Map<Long, Chunk> chunks = new ConcurrentHashMap<>();
|
||||
|
||||
private boolean autoChunkLoad;
|
||||
|
||||
protected InstanceContainer(UUID uniqueId, File folder) {
|
||||
super(uniqueId);
|
||||
this.folder = folder;
|
||||
@ -35,7 +38,7 @@ public class InstanceContainer extends Instance {
|
||||
Chunk chunk = getChunkAt(x, z);
|
||||
synchronized (chunk) {
|
||||
chunk.setBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId);
|
||||
PacketWriter.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> {
|
||||
PacketWriter.writeCallbackPacket(chunk.getFreshPartialDataPacket(), buffer -> {
|
||||
chunk.setFullDataPacket(buffer);
|
||||
sendChunkUpdate(chunk);
|
||||
});
|
||||
@ -46,8 +49,8 @@ public class InstanceContainer extends Instance {
|
||||
public synchronized void setBlock(int x, int y, int z, String blockId) {
|
||||
Chunk chunk = getChunkAt(x, z);
|
||||
synchronized (chunk) {
|
||||
chunk.setBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId);
|
||||
PacketWriter.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> {
|
||||
chunk.setCustomBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId);
|
||||
PacketWriter.writeCallbackPacket(chunk.getFreshPartialDataPacket(), buffer -> {
|
||||
chunk.setFullDataPacket(buffer);
|
||||
sendChunkUpdate(chunk);
|
||||
});
|
||||
@ -56,9 +59,16 @@ public class InstanceContainer extends Instance {
|
||||
|
||||
// TODO deplace
|
||||
@Override
|
||||
public void breakBlock(Player player, BlockPosition blockPosition, short blockId) {
|
||||
BlockBreakEvent blockBreakEvent = new BlockBreakEvent(blockPosition);
|
||||
player.callEvent(BlockBreakEvent.class, blockBreakEvent);
|
||||
public void breakBlock(Player player, BlockPosition blockPosition) {
|
||||
Chunk chunk = getChunkAt(blockPosition);
|
||||
short blockId = chunk.getBlockId((byte) (blockPosition.getX() % 16), (byte) blockPosition.getY(), (byte) (blockPosition.getZ() % 16));
|
||||
if (blockId == 0) {
|
||||
sendChunkUpdate(player, chunk);
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerBlockBreakEvent blockBreakEvent = new PlayerBlockBreakEvent(blockPosition);
|
||||
player.callEvent(PlayerBlockBreakEvent.class, blockBreakEvent);
|
||||
if (!blockBreakEvent.isCancelled()) {
|
||||
// TODO blockbreak setBlock result
|
||||
int x = blockPosition.getX();
|
||||
@ -71,16 +81,16 @@ public class InstanceContainer extends Instance {
|
||||
particlePacket.x = x + 0.5f;
|
||||
particlePacket.y = y;
|
||||
particlePacket.z = z + 0.5f;
|
||||
particlePacket.offsetX = 0.4f;
|
||||
particlePacket.offsetY = 0.6f;
|
||||
particlePacket.offsetZ = 0.4f;
|
||||
particlePacket.offsetX = 0.45f;
|
||||
particlePacket.offsetY = 0.55f;
|
||||
particlePacket.offsetZ = 0.45f;
|
||||
particlePacket.particleData = 0.3f;
|
||||
particlePacket.particleCount = 75;
|
||||
particlePacket.particleCount = 100;
|
||||
particlePacket.blockId = blockId;
|
||||
player.getPlayerConnection().sendPacket(particlePacket);
|
||||
player.sendPacketToViewers(particlePacket);
|
||||
} else {
|
||||
sendChunkUpdate(player, getChunkAt(blockPosition));
|
||||
sendChunkUpdate(player, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@ -95,9 +105,24 @@ public class InstanceContainer extends Instance {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadOptionalChunk(int chunkX, int chunkZ, Consumer<Chunk> callback) {
|
||||
Chunk chunk = getChunk(chunkX, chunkZ);
|
||||
if (chunk != null) {
|
||||
if (callback != null)
|
||||
callback.accept(chunk);
|
||||
} else {
|
||||
if (hasEnabledAutoChunkLoad()) {
|
||||
retrieveChunk(chunkX, chunkZ, callback);
|
||||
} else {
|
||||
callback.accept(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk getChunk(int chunkX, int chunkZ) {
|
||||
return chunks.get(getChunkIndex(chunkX, chunkZ));
|
||||
return chunks.get(ChunkUtils.getChunkIndex(chunkX, chunkZ));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -123,54 +148,6 @@ public class InstanceContainer extends Instance {
|
||||
return new ChunkBatch(this, chunk);
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public void addEntity(Entity entity) {
|
||||
Instance lastInstance = entity.getInstance();
|
||||
if (lastInstance != null && lastInstance != this) {
|
||||
lastInstance.removeEntity(entity);
|
||||
}
|
||||
|
||||
// TODO based on distance with players
|
||||
getPlayers().forEach(p -> entity.addViewer(p));
|
||||
|
||||
if (entity instanceof Player) {
|
||||
Player player = (Player) entity;
|
||||
sendChunks(player);
|
||||
getObjectEntities().forEach(objectEntity -> objectEntity.addViewer(player));
|
||||
getCreatures().forEach(entityCreature -> entityCreature.addViewer(player));
|
||||
getPlayers().forEach(p -> p.addViewer(player));
|
||||
}
|
||||
|
||||
Chunk chunk = getChunkAt(entity.getPosition());
|
||||
chunk.addEntity(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntity(Entity entity) {
|
||||
Instance entityInstance = entity.getInstance();
|
||||
if (entityInstance == null || entityInstance != this)
|
||||
return;
|
||||
|
||||
entity.getViewers().forEach(p -> entity.removeViewer(p));
|
||||
|
||||
if (!(entity instanceof Player)) {
|
||||
DestroyEntitiesPacket destroyEntitiesPacket = new DestroyEntitiesPacket();
|
||||
destroyEntitiesPacket.entityIds = new int[]{entity.getEntityId()};
|
||||
|
||||
entity.getViewers().forEach(p -> p.getPlayerConnection().sendPacket(destroyEntitiesPacket)); // TODO destroy batch
|
||||
} else {
|
||||
// TODO optimize (cache all entities that the player see)
|
||||
Player player = (Player) entity;
|
||||
getObjectEntities().forEach(objectEntity -> objectEntity.removeViewer(player));
|
||||
getCreatures().forEach(entityCreature -> entityCreature.removeViewer(player));
|
||||
getPlayers().forEach(p -> p.removeViewer(player));
|
||||
|
||||
}
|
||||
|
||||
Chunk chunk = getChunkAt(entity.getPosition());
|
||||
chunk.removeEntity(entity);
|
||||
}*/
|
||||
|
||||
@Override
|
||||
public void sendChunkUpdate(Player player, Chunk chunk) {
|
||||
Buffer chunkData = chunk.getFullDataPacket();
|
||||
@ -180,7 +157,7 @@ public class InstanceContainer extends Instance {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void retrieveChunk(int chunkX, int chunkZ, Consumer<Chunk> callback) {
|
||||
protected void retrieveChunk(int chunkX, int chunkZ, Consumer<Chunk> callback) {
|
||||
if (folder != null) {
|
||||
// Load from file if possible
|
||||
CHUNK_LOADER_IO.loadChunk(chunkX, chunkZ, this, chunk -> {
|
||||
@ -189,6 +166,7 @@ public class InstanceContainer extends Instance {
|
||||
callback.accept(chunk);
|
||||
});
|
||||
} else {
|
||||
// Folder isn't defined, create new chunk
|
||||
createChunk(chunkX, chunkZ, callback);
|
||||
}
|
||||
}
|
||||
@ -212,40 +190,54 @@ public class InstanceContainer extends Instance {
|
||||
sendChunkUpdate(getPlayers(), chunk);
|
||||
|
||||
// Update for shared instances
|
||||
this.sharedInstances.forEach(sharedInstance -> {
|
||||
if (!sharedInstance.getPlayers().isEmpty())
|
||||
sendChunkUpdate(sharedInstance.getPlayers(), chunk);
|
||||
});
|
||||
if (!sharedInstances.isEmpty())
|
||||
this.sharedInstances.forEach(sharedInstance -> {
|
||||
Set<Player> instancePlayers = sharedInstance.getPlayers();
|
||||
if (!instancePlayers.isEmpty())
|
||||
sendChunkUpdate(instancePlayers, chunk);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChunks(Player player) {
|
||||
for (Chunk chunk : getChunks()) {
|
||||
Buffer chunkData = chunk.getFullDataPacket();
|
||||
if (chunkData == null) {
|
||||
PacketWriter.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> {
|
||||
buffer.getData().retain(1).markReaderIndex();
|
||||
player.getPlayerConnection().sendUnencodedPacket(buffer);
|
||||
buffer.getData().resetReaderIndex();
|
||||
chunk.setFullDataPacket(buffer);
|
||||
});
|
||||
} else {
|
||||
chunkData.getData().retain(1).markReaderIndex();
|
||||
player.getPlayerConnection().sendUnencodedPacket(chunkData);
|
||||
chunkData.getData().resetReaderIndex();
|
||||
}
|
||||
sendChunk(player, chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChunk(Player player, Chunk chunk) {
|
||||
Buffer chunkData = chunk.getFullDataPacket();
|
||||
if (chunkData == null) {
|
||||
PacketWriter.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> {
|
||||
buffer.getData().retain(1).markReaderIndex();
|
||||
player.getPlayerConnection().sendUnencodedPacket(buffer);
|
||||
buffer.getData().resetReaderIndex();
|
||||
chunk.setFullDataPacket(buffer);
|
||||
});
|
||||
} else {
|
||||
chunkData.getData().retain(1).markReaderIndex();
|
||||
player.getPlayerConnection().sendUnencodedPacket(chunkData);
|
||||
chunkData.getData().resetReaderIndex();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableAutoChunkLoad(boolean enable) {
|
||||
this.autoChunkLoad = enable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEnabledAutoChunkLoad() {
|
||||
return autoChunkLoad;
|
||||
}
|
||||
|
||||
protected void addSharedInstance(SharedInstance sharedInstance) {
|
||||
this.sharedInstances.add(sharedInstance);
|
||||
}
|
||||
|
||||
private void cacheChunk(Chunk chunk) {
|
||||
//this.objectEntities.addCollection(chunk.objectEntities);
|
||||
//this.creatures.addCollection(chunk.creatures);
|
||||
//this.players.addCollection(chunk.players);
|
||||
this.chunks.put(getChunkIndex(chunk.getChunkX(), chunk.getChunkZ()), chunk);
|
||||
this.chunks.put(ChunkUtils.getChunkIndex(chunk.getChunkX(), chunk.getChunkZ()), chunk);
|
||||
}
|
||||
|
||||
public void setChunkGenerator(ChunkGenerator chunkGenerator) {
|
||||
|
@ -21,8 +21,8 @@ public class SharedInstance extends Instance {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void breakBlock(Player player, BlockPosition blockPosition, short blockId) {
|
||||
instanceContainer.breakBlock(player, blockPosition, blockId);
|
||||
public void breakBlock(Player player, BlockPosition blockPosition) {
|
||||
instanceContainer.breakBlock(player, blockPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -30,6 +30,11 @@ public class SharedInstance extends Instance {
|
||||
instanceContainer.loadChunk(chunkX, chunkZ, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadOptionalChunk(int chunkX, int chunkZ, Consumer<Chunk> callback) {
|
||||
instanceContainer.loadOptionalChunk(chunkX, chunkZ, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Chunk getChunk(int chunkX, int chunkZ) {
|
||||
return instanceContainer.getChunk(chunkX, chunkZ);
|
||||
@ -60,12 +65,6 @@ public class SharedInstance extends Instance {
|
||||
return instanceContainer.getChunks();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUniqueId() {
|
||||
// FIXME: share same UUID ?
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getFolder() {
|
||||
return instanceContainer.getFolder();
|
||||
@ -96,6 +95,21 @@ public class SharedInstance extends Instance {
|
||||
instanceContainer.sendChunks(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChunk(Player player, Chunk chunk) {
|
||||
instanceContainer.sendChunk(player, chunk);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableAutoChunkLoad(boolean enable) {
|
||||
instanceContainer.enableAutoChunkLoad(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEnabledAutoChunkLoad() {
|
||||
return instanceContainer.hasEnabledAutoChunkLoad();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(int x, int y, int z, short blockId) {
|
||||
instanceContainer.setBlock(x, y, z, blockId);
|
||||
|
@ -1,7 +1,7 @@
|
||||
package fr.themode.minestom.listener;
|
||||
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.event.BlockPlaceEvent;
|
||||
import fr.themode.minestom.event.PlayerBlockPlaceEvent;
|
||||
import fr.themode.minestom.instance.Chunk;
|
||||
import fr.themode.minestom.instance.Instance;
|
||||
import fr.themode.minestom.net.packet.client.play.ClientPlayerBlockPlacementPacket;
|
||||
@ -23,9 +23,10 @@ public class BlockPlacementListener {
|
||||
int offsetZ = blockFace == ClientPlayerDiggingPacket.BlockFace.NORTH ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.SOUTH ? 1 : 0;
|
||||
|
||||
blockPosition.add(offsetX, offsetY, offsetZ);
|
||||
BlockPlaceEvent blockPlaceEvent = new BlockPlaceEvent((short) 10, blockPosition, packet.hand);
|
||||
player.callEvent(BlockPlaceEvent.class, blockPlaceEvent);
|
||||
if (!blockPlaceEvent.isCancelled()) {
|
||||
PlayerBlockPlaceEvent playerBlockPlaceEvent = new PlayerBlockPlaceEvent((short) 10, blockPosition, packet.hand);
|
||||
player.callEvent(PlayerBlockPlaceEvent.class, playerBlockPlaceEvent);
|
||||
// TODO if player does not have block in hand, then call InteractEvent with blockPosition
|
||||
if (!playerBlockPlaceEvent.isCancelled()) {
|
||||
instance.setBlock(blockPosition, "custom_block");
|
||||
// TODO consume block in hand for survival players
|
||||
} else {
|
||||
|
@ -2,7 +2,7 @@ package fr.themode.minestom.listener;
|
||||
|
||||
import fr.themode.minestom.entity.GameMode;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.event.StartDiggingEvent;
|
||||
import fr.themode.minestom.event.PlayerStartDiggingEvent;
|
||||
import fr.themode.minestom.instance.CustomBlock;
|
||||
import fr.themode.minestom.instance.Instance;
|
||||
import fr.themode.minestom.net.packet.client.play.ClientPlayerDiggingPacket;
|
||||
@ -27,9 +27,9 @@ public class PlayerDiggingListener {
|
||||
if (instance != null) {
|
||||
CustomBlock customBlock = instance.getCustomBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ());
|
||||
if (customBlock != null) {
|
||||
StartDiggingEvent startDiggingEvent = new StartDiggingEvent(customBlock);
|
||||
player.callEvent(StartDiggingEvent.class, startDiggingEvent);
|
||||
if (!startDiggingEvent.isCancelled()) {
|
||||
PlayerStartDiggingEvent playerStartDiggingEvent = new PlayerStartDiggingEvent(customBlock);
|
||||
player.callEvent(PlayerStartDiggingEvent.class, playerStartDiggingEvent);
|
||||
if (!playerStartDiggingEvent.isCancelled()) {
|
||||
player.refreshTargetBlock(customBlock, blockPosition);
|
||||
}
|
||||
addEffect(player);
|
||||
@ -52,8 +52,7 @@ public class PlayerDiggingListener {
|
||||
} else {
|
||||
Instance instance = player.getInstance();
|
||||
if (instance != null) {
|
||||
short blockId = instance.getBlockId(blockPosition);
|
||||
instance.breakBlock(player, blockPosition, blockId);
|
||||
instance.breakBlock(player, blockPosition);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -1,25 +1,14 @@
|
||||
package fr.themode.minestom.listener;
|
||||
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.event.PlayerRespawnEvent;
|
||||
import fr.themode.minestom.net.packet.client.play.ClientStatusPacket;
|
||||
import fr.themode.minestom.net.packet.server.play.RespawnPacket;
|
||||
|
||||
public class StatusListener {
|
||||
|
||||
public static void listener(ClientStatusPacket packet, Player player) {
|
||||
switch (packet.action) {
|
||||
case PERFORM_RESPAWN:
|
||||
RespawnPacket respawnPacket = new RespawnPacket();
|
||||
respawnPacket.dimension = player.getDimension();
|
||||
respawnPacket.gameMode = player.getGameMode();
|
||||
respawnPacket.levelType = player.getLevelType();
|
||||
player.getPlayerConnection().sendPacket(respawnPacket);
|
||||
PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(player.getPosition());
|
||||
player.callEvent(PlayerRespawnEvent.class, respawnEvent);
|
||||
player.refreshIsDead(false);
|
||||
player.teleport(respawnEvent.getRespawnPosition());
|
||||
player.getInventory().update();
|
||||
player.respawn();
|
||||
break;
|
||||
case REQUEST_STATS:
|
||||
// TODO stats
|
||||
|
@ -1,10 +1,10 @@
|
||||
package fr.themode.minestom.listener;
|
||||
|
||||
import fr.themode.minestom.entity.Entity;
|
||||
import fr.themode.minestom.entity.EntityCreature;
|
||||
import fr.themode.minestom.entity.LivingEntity;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.event.AttackEvent;
|
||||
import fr.themode.minestom.event.InteractEvent;
|
||||
import fr.themode.minestom.event.PlayerInteractEvent;
|
||||
import fr.themode.minestom.net.packet.client.play.ClientUseEntityPacket;
|
||||
|
||||
public class UseEntityListener {
|
||||
@ -15,17 +15,17 @@ public class UseEntityListener {
|
||||
return;
|
||||
ClientUseEntityPacket.Type type = packet.type;
|
||||
if (type == ClientUseEntityPacket.Type.ATTACK) {
|
||||
if (entity instanceof EntityCreature && ((EntityCreature) entity).isDead()) // Can't attack dead entities
|
||||
if (entity instanceof LivingEntity && ((LivingEntity) entity).isDead()) // Can't attack dead entities
|
||||
return;
|
||||
|
||||
AttackEvent attackEvent = new AttackEvent(entity);
|
||||
player.callEvent(AttackEvent.class, attackEvent);
|
||||
} else if (type == ClientUseEntityPacket.Type.INTERACT) {
|
||||
InteractEvent interactEvent = new InteractEvent(entity);
|
||||
player.callEvent(InteractEvent.class, interactEvent);
|
||||
PlayerInteractEvent playerInteractEvent = new PlayerInteractEvent(entity, packet.hand);
|
||||
player.callEvent(PlayerInteractEvent.class, playerInteractEvent);
|
||||
} else {
|
||||
InteractEvent interactEvent = new InteractEvent(entity); // TODO find difference with INTERACT
|
||||
player.callEvent(InteractEvent.class, interactEvent);
|
||||
PlayerInteractEvent playerInteractEvent = new PlayerInteractEvent(entity, packet.hand); // TODO find difference with INTERACT
|
||||
player.callEvent(PlayerInteractEvent.class, playerInteractEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package fr.themode.minestom.listener;
|
||||
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.event.UseItemEvent;
|
||||
import fr.themode.minestom.event.PlayerUseItemEvent;
|
||||
import fr.themode.minestom.inventory.PlayerInventory;
|
||||
import fr.themode.minestom.item.ItemStack;
|
||||
import fr.themode.minestom.net.packet.client.play.ClientUseItemPacket;
|
||||
@ -12,8 +12,8 @@ public class UseItemListener {
|
||||
PlayerInventory inventory = player.getInventory();
|
||||
Player.Hand hand = packet.hand;
|
||||
ItemStack itemStack = hand == Player.Hand.MAIN ? inventory.getItemInMainHand() : inventory.getItemInOffHand();
|
||||
UseItemEvent useItemEvent = new UseItemEvent(hand, itemStack);
|
||||
player.callEvent(UseItemEvent.class, useItemEvent);
|
||||
PlayerUseItemEvent playerUseItemEvent = new PlayerUseItemEvent(hand, itemStack);
|
||||
player.callEvent(PlayerUseItemEvent.class, playerUseItemEvent);
|
||||
|
||||
// TODO check if item in main or off hand is food or item with animation (bow/crossbow/riptide)
|
||||
// TODO in material enum?
|
||||
|
@ -80,7 +80,7 @@ public class LoginStartPacket implements ClientPreplayPacket {
|
||||
joinGamePacket.dimension = dimension;
|
||||
joinGamePacket.maxPlayers = 0; // Unused
|
||||
joinGamePacket.levelType = levelType;
|
||||
joinGamePacket.viewDistance = 14;
|
||||
joinGamePacket.viewDistance = Main.CHUNK_VIEW_DISTANCE;
|
||||
joinGamePacket.reducedDebugInfo = false;
|
||||
connection.sendPacket(joinGamePacket);
|
||||
|
||||
|
@ -82,11 +82,11 @@ public class ChunkDataPacket implements ServerPacket {
|
||||
Utils.writeVarInt(buffer, blockEntities.size());
|
||||
|
||||
for (Integer index : blockEntities) {
|
||||
BlockPosition blockPosition = SerializerUtils.indexToBlockPosition(index, chunk.getChunkX(), chunk.getChunkZ());
|
||||
BlockPosition blockPosition = SerializerUtils.indexToChunkBlockPosition(index);
|
||||
CompoundTag blockEntity = new CompoundTag();
|
||||
blockEntity.put("x", new DoubleTag(blockPosition.getX()));
|
||||
blockEntity.put("x", new DoubleTag(blockPosition.getX() + 16 * chunk.getChunkX()));
|
||||
blockEntity.put("y", new DoubleTag(blockPosition.getY()));
|
||||
blockEntity.put("z", new DoubleTag(blockPosition.getZ()));
|
||||
blockEntity.put("z", new DoubleTag(blockPosition.getZ() + 16 * chunk.getChunkZ()));
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
try {
|
||||
blockEntity.serialize(new DataOutputStream(os), 100);
|
||||
|
@ -12,6 +12,7 @@ public class SpawnPlayerPacket implements ServerPacket {
|
||||
public int entityId;
|
||||
public UUID playerUuid;
|
||||
public Position position;
|
||||
public Buffer metadata;
|
||||
|
||||
@Override
|
||||
public void write(Buffer buffer) {
|
||||
@ -23,7 +24,11 @@ public class SpawnPlayerPacket implements ServerPacket {
|
||||
buffer.putDouble(position.getZ());
|
||||
buffer.putByte((byte) (position.getYaw() * 256f / 360f));
|
||||
buffer.putByte((byte) (position.getPitch() * 256f / 360f));
|
||||
buffer.putByte((byte) 0xff); // TODO Metadata
|
||||
if (metadata != null) {
|
||||
buffer.putBuffer(metadata);
|
||||
} else {
|
||||
buffer.putByte((byte) 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,20 @@
|
||||
package fr.themode.minestom.net.packet.server.play;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||
|
||||
public class UnloadChunkPacket implements ServerPacket {
|
||||
|
||||
public int chunkX, chunkZ;
|
||||
|
||||
@Override
|
||||
public void write(Buffer buffer) {
|
||||
buffer.putInt(chunkX);
|
||||
buffer.putInt(chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return 0x1D;
|
||||
}
|
||||
}
|
35
src/main/java/fr/themode/minestom/utils/ChunkUtils.java
Normal file
35
src/main/java/fr/themode/minestom/utils/ChunkUtils.java
Normal file
@ -0,0 +1,35 @@
|
||||
package fr.themode.minestom.utils;
|
||||
|
||||
import fr.themode.minestom.Main;
|
||||
|
||||
public class ChunkUtils {
|
||||
|
||||
public static long getChunkIndex(int chunkX, int chunkZ) {
|
||||
return (((long) chunkX) << 32) | (chunkZ & 0xffffffffL);
|
||||
}
|
||||
|
||||
public static int[] getChunkCoord(long index) {
|
||||
int chunkX = (int) (index >> 32);
|
||||
int chunkZ = (int) index;
|
||||
return new int[]{chunkX, chunkZ};
|
||||
}
|
||||
|
||||
public static long[] getVisibleChunks(final Position position) {
|
||||
final int viewDistance = Main.CHUNK_VIEW_DISTANCE;
|
||||
|
||||
long[] visibleChunks = new long[MathUtils.square(viewDistance + 1)];
|
||||
final int startLoop = -(viewDistance / 2);
|
||||
final int endLoop = viewDistance / 2 + 1;
|
||||
int counter = 0;
|
||||
for (int x = startLoop; x < endLoop; x++) {
|
||||
for (int z = startLoop; z < endLoop; z++) {
|
||||
int chunkX = Math.floorDiv((int) (position.getX() + 16 * x), 16);
|
||||
int chunkZ = Math.floorDiv((int) (position.getZ() + 16 * z), 16);
|
||||
visibleChunks[counter] = getChunkIndex(chunkX, chunkZ);
|
||||
counter++;
|
||||
}
|
||||
}
|
||||
return visibleChunks;
|
||||
}
|
||||
|
||||
}
|
@ -2,6 +2,10 @@ package fr.themode.minestom.utils;
|
||||
|
||||
public class MathUtils {
|
||||
|
||||
public static int square(int num) {
|
||||
return num * num;
|
||||
}
|
||||
|
||||
public static float square(float num) {
|
||||
return num * num;
|
||||
}
|
||||
|
@ -21,10 +21,11 @@ public class Position {
|
||||
this(0, 0, 0);
|
||||
}
|
||||
|
||||
public void add(float x, float y, float z) {
|
||||
public Position add(float x, float y, float z) {
|
||||
this.x += x;
|
||||
this.y += y;
|
||||
this.z += z;
|
||||
return this;
|
||||
}
|
||||
|
||||
public float getDistance(Position position) {
|
||||
|
@ -32,11 +32,22 @@ public class SerializerUtils {
|
||||
return new byte[]{x, y, z};
|
||||
}
|
||||
|
||||
public static BlockPosition indexToBlockPosition(int index, int chunkX, int chunkZ) {
|
||||
public static BlockPosition indexToChunkBlockPosition(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 * chunkX, y, z + 16 * chunkZ);
|
||||
return new BlockPosition(x, y, z);
|
||||
}
|
||||
|
||||
public static long positionToLong(int x, int y, int z) {
|
||||
return (((long) x & 0x3FFFFFF) << 38) | (((long) z & 0x3FFFFFF) << 12) | ((long) y & 0xFFF);
|
||||
}
|
||||
|
||||
public static BlockPosition longToBlockPosition(long value) {
|
||||
int x = (int) (value >> 38);
|
||||
int y = (int) (value & 0xFFF);
|
||||
int z = (int) (value << 26 >> 38);
|
||||
return new BlockPosition(x, y, z);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -121,7 +121,7 @@ public class Utils {
|
||||
}
|
||||
|
||||
public static void writePosition(Buffer buffer, int x, int y, int z) {
|
||||
buffer.putLong((((long) x & 0x3FFFFFF) << 38) | (((long) z & 0x3FFFFFF) << 12) | ((long) y & 0xFFF));
|
||||
buffer.putLong(SerializerUtils.positionToLong(x, y, z));
|
||||
}
|
||||
|
||||
public static void writePosition(Buffer buffer, BlockPosition blockPosition) {
|
||||
@ -129,11 +129,7 @@ public class Utils {
|
||||
}
|
||||
|
||||
public static BlockPosition readPosition(Buffer buffer) {
|
||||
long val = buffer.getLong();
|
||||
int x = (int) (val >> 38);
|
||||
int y = (int) (val & 0xFFF);
|
||||
int z = (int) (val << 26 >> 38);
|
||||
return new BlockPosition(x, y, z);
|
||||
return SerializerUtils.longToBlockPosition(buffer.getLong());
|
||||
}
|
||||
|
||||
public static void writeUuid(Buffer buffer, UUID uuid) {
|
||||
|
Loading…
Reference in New Issue
Block a user