mirror of
https://github.com/Minestom/Minestom.git
synced 2025-03-02 11:21:15 +01:00
Update
This commit is contained in:
parent
4b1fac6cd4
commit
0a732034c2
@ -8,7 +8,6 @@ import fr.adamaq01.ozao.net.server.backend.tcp.TCPServer;
|
||||
import fr.themode.minestom.entity.EntityManager;
|
||||
import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.instance.BlockManager;
|
||||
import fr.themode.minestom.instance.Instance;
|
||||
import fr.themode.minestom.instance.InstanceManager;
|
||||
import fr.themode.minestom.instance.demo.StoneBlock;
|
||||
import fr.themode.minestom.listener.PacketListenerManager;
|
||||
@ -30,8 +29,8 @@ public class Main {
|
||||
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
|
||||
public static final int CHUNK_VIEW_DISTANCE = 5;
|
||||
public static final int ENTITY_VIEW_DISTANCE = 2;
|
||||
|
||||
// Networking
|
||||
private static ConnectionManager connectionManager;
|
||||
@ -68,16 +67,9 @@ public class Main {
|
||||
Player player = connectionManager.getPlayer(packetProcessor.getPlayerConnection(connection));
|
||||
if (player != null) {
|
||||
|
||||
Instance instance = player.getInstance();
|
||||
|
||||
|
||||
if (instance != null) {
|
||||
instance.removeEntity(player);
|
||||
}
|
||||
|
||||
player.remove();
|
||||
|
||||
connectionManager.removePlayer(packetProcessor.getPlayerConnection(connection));
|
||||
connectionManager.removePlayer(player.getPlayerConnection());
|
||||
}
|
||||
packetProcessor.removePlayerConnection(connection);
|
||||
}
|
||||
|
@ -54,19 +54,25 @@ public interface Viewable {
|
||||
|
||||
default void sendPacketToViewersAndSelf(ServerPacket packet) {
|
||||
if (this instanceof Player) {
|
||||
PacketWriter.writeCallbackPacket(packet, buffer -> {
|
||||
int size = getViewers().size();
|
||||
buffer.getData().retain(size + 1).markReaderIndex();
|
||||
((Player) this).getPlayerConnection().writeUnencodedPacket(buffer);
|
||||
buffer.getData().resetReaderIndex();
|
||||
if (size != 0) {
|
||||
for (Player viewer : getViewers()) {
|
||||
buffer.getData().resetReaderIndex();
|
||||
viewer.getPlayerConnection().writeUnencodedPacket(buffer);
|
||||
}
|
||||
}
|
||||
});
|
||||
UNSAFE_sendPacketToViewersAndSelf(packet);
|
||||
} else {
|
||||
sendPacketToViewers(packet);
|
||||
}
|
||||
}
|
||||
|
||||
private void UNSAFE_sendPacketToViewersAndSelf(ServerPacket packet) {
|
||||
PacketWriter.writeCallbackPacket(packet, buffer -> {
|
||||
int size = getViewers().size();
|
||||
buffer.getData().retain(size + 1).markReaderIndex();
|
||||
((Player) this).getPlayerConnection().writeUnencodedPacket(buffer);
|
||||
buffer.getData().resetReaderIndex();
|
||||
if (size != 0) {
|
||||
for (Player viewer : getViewers()) {
|
||||
buffer.getData().resetReaderIndex();
|
||||
viewer.getPlayerConnection().writeUnencodedPacket(buffer);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,14 @@ package fr.themode.minestom.chat;
|
||||
|
||||
public class Chat {
|
||||
|
||||
|
||||
public static String rawText(String text) {
|
||||
return "{\"text\": \"" + text + "\"}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Different types of chat message:
|
||||
* Colored one (simplest)
|
||||
* Colored + event (hover/click)
|
||||
*/
|
||||
}
|
||||
|
@ -11,9 +11,8 @@ import fr.themode.minestom.event.Event;
|
||||
import fr.themode.minestom.instance.Chunk;
|
||||
import fr.themode.minestom.instance.Instance;
|
||||
import fr.themode.minestom.net.packet.server.play.*;
|
||||
import fr.themode.minestom.utils.Position;
|
||||
import fr.themode.minestom.utils.Utils;
|
||||
import fr.themode.minestom.utils.Vector;
|
||||
import fr.themode.minestom.utils.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -60,7 +59,7 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
protected long velocityTime; // Reset velocity to 0 after countdown
|
||||
|
||||
// Synchronization
|
||||
private long synchronizationDelay = 2000; // In ms
|
||||
private long synchronizationDelay = 1500; // In ms
|
||||
private long lastSynchronizationTime;
|
||||
|
||||
// Metadata
|
||||
@ -107,7 +106,7 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
|
||||
public void teleport(Position position, Runnable callback) {
|
||||
if (instance == null)
|
||||
return;
|
||||
throw new IllegalStateException("You need to use Entity#setInstance before teleporting an entity!");
|
||||
|
||||
Runnable runnable = () -> {
|
||||
refreshPosition(position.getX(), position.getY(), position.getZ());
|
||||
@ -219,7 +218,7 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
sendPositionSynchronization();
|
||||
}
|
||||
|
||||
this.lastUpdate = System.currentTimeMillis();
|
||||
this.lastUpdate = time;
|
||||
}
|
||||
|
||||
if (shouldRemove()) {
|
||||
@ -300,23 +299,31 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
return getPosition().getDistance(entity.getPosition());
|
||||
}
|
||||
|
||||
public Entity getVehicle() {
|
||||
return vehicle;
|
||||
}
|
||||
|
||||
public void addPassenger(Entity entity) {
|
||||
// TODO if entity already has a vehicle, leave it before?
|
||||
if (instance == null)
|
||||
throw new IllegalStateException("You need to set an instance using Entity#setInstance");
|
||||
if (entity.getVehicle() != null) {
|
||||
entity.getVehicle().removePassenger(entity);
|
||||
}
|
||||
|
||||
this.passengers.add(entity);
|
||||
entity.vehicle = this;
|
||||
if (instance != null) {
|
||||
SetPassengersPacket passengersPacket = new SetPassengersPacket();
|
||||
passengersPacket.vehicleEntityId = getEntityId();
|
||||
|
||||
int[] passengers = new int[this.passengers.size()];
|
||||
int counter = 0;
|
||||
for (Entity passenger : this.passengers) {
|
||||
passengers[counter++] = passenger.getEntityId();
|
||||
}
|
||||
sendPassengersPacket();
|
||||
}
|
||||
|
||||
passengersPacket.passengersId = passengers;
|
||||
sendPacketToViewers(passengersPacket);
|
||||
}
|
||||
public void removePassenger(Entity entity) {
|
||||
if (instance == null)
|
||||
throw new IllegalStateException("You need to set an instance using Entity#setInstance");
|
||||
if (!passengers.contains(entity))
|
||||
return;
|
||||
this.passengers.remove(entity);
|
||||
entity.vehicle = null;
|
||||
sendPassengersPacket();
|
||||
}
|
||||
|
||||
public boolean hasPassenger() {
|
||||
@ -327,6 +334,20 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
return Collections.unmodifiableSet(passengers);
|
||||
}
|
||||
|
||||
protected void sendPassengersPacket() {
|
||||
SetPassengersPacket passengersPacket = new SetPassengersPacket();
|
||||
passengersPacket.vehicleEntityId = getEntityId();
|
||||
|
||||
int[] passengers = new int[this.passengers.size()];
|
||||
int counter = 0;
|
||||
for (Entity passenger : this.passengers) {
|
||||
passengers[counter++] = passenger.getEntityId();
|
||||
}
|
||||
|
||||
passengersPacket.passengersId = passengers;
|
||||
sendPacketToViewersAndSelf(passengersPacket);
|
||||
}
|
||||
|
||||
public void triggerStatus(byte status) {
|
||||
EntityStatusPacket statusPacket = new EntityStatusPacket();
|
||||
statusPacket.entityId = getEntityId();
|
||||
@ -373,7 +394,44 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
if (this instanceof Player)
|
||||
((Player) this).onChunkChange(lastChunk, newChunk); // Refresh loaded chunk
|
||||
|
||||
// TODO compare with viewers and remove if too far away
|
||||
// Refresh entity viewable list
|
||||
long[] lastVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()), Main.ENTITY_VIEW_DISTANCE);
|
||||
long[] updatedVisibleChunksEntity = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), Main.ENTITY_VIEW_DISTANCE);
|
||||
|
||||
int[] oldChunksEntity = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunksEntity, updatedVisibleChunksEntity);
|
||||
for (int index : oldChunksEntity) {
|
||||
int[] chunkPos = ChunkUtils.getChunkCoord(lastVisibleChunksEntity[index]);
|
||||
Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]);
|
||||
if (chunk == null)
|
||||
continue;
|
||||
instance.getChunkEntities(chunk).forEach(entity -> {
|
||||
if (entity instanceof Player) {
|
||||
Player player = (Player) entity;
|
||||
removeViewer(player);
|
||||
if (this instanceof Player) {
|
||||
player.removeViewer((Player) this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
int[] newChunksEntity = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunksEntity, lastVisibleChunksEntity);
|
||||
for (int index : newChunksEntity) {
|
||||
int[] chunkPos = ChunkUtils.getChunkCoord(updatedVisibleChunksEntity[index]);
|
||||
Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]);
|
||||
if (chunk == null)
|
||||
continue;
|
||||
instance.getChunkEntities(chunk).forEach(entity -> {
|
||||
if (entity instanceof Player) {
|
||||
Player player = (Player) entity;
|
||||
addViewer(player);
|
||||
if (this instanceof Player) {
|
||||
player.addViewer((Player) this);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -448,12 +506,8 @@ public abstract class Entity implements Viewable, DataContainer {
|
||||
EntityMetaDataPacket metaDataPacket = new EntityMetaDataPacket();
|
||||
metaDataPacket.entityId = getEntityId();
|
||||
metaDataPacket.data = buffer;
|
||||
if (this instanceof Player) {
|
||||
Player player = (Player) this;
|
||||
player.sendPacketToViewersAndSelf(metaDataPacket);
|
||||
} else {
|
||||
sendPacketToViewers(metaDataPacket);
|
||||
}
|
||||
|
||||
sendPacketToViewersAndSelf(metaDataPacket);
|
||||
}
|
||||
|
||||
private void fillMetadataIndex(Buffer buffer, int index) {
|
||||
|
@ -42,7 +42,7 @@ public class EntityManager {
|
||||
Instance spawningInstance = loginEvent.getSpawningInstance() == null ? instanceManager.createInstanceContainer() : loginEvent.getSpawningInstance();
|
||||
Position position = playerCache.getPosition();
|
||||
|
||||
long[] visibleChunks = ChunkUtils.getVisibleChunks(position);
|
||||
long[] visibleChunks = ChunkUtils.getChunksInRange(position, Main.CHUNK_VIEW_DISTANCE);
|
||||
for (int i = 0; i < visibleChunks.length; i++) {
|
||||
int[] chunkPos = ChunkUtils.getChunkCoord(visibleChunks[i]);
|
||||
int chunkX = chunkPos[0];
|
||||
|
@ -142,7 +142,7 @@ public class Player extends LivingEntity {
|
||||
|
||||
setEventCallback(PlayerSpawnEvent.class, event -> {
|
||||
System.out.println("SPAWN");
|
||||
setGameMode(GameMode.CREATIVE);
|
||||
setGameMode(GameMode.SURVIVAL);
|
||||
teleport(new Position(0, 66, 0));
|
||||
|
||||
/*ChickenCreature chickenCreature = new ChickenCreature();
|
||||
@ -434,12 +434,9 @@ public class Player extends LivingEntity {
|
||||
float dz = newChunk.getChunkZ() - lastChunk.getChunkZ();
|
||||
double distance = Math.sqrt(dx * dx + dz * dz);
|
||||
boolean isFar = distance >= Main.CHUNK_VIEW_DISTANCE / 2;
|
||||
if (isFar) {
|
||||
updatePlayerPosition();
|
||||
}
|
||||
|
||||
long[] lastVisibleChunks = ChunkUtils.getVisibleChunks(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()));
|
||||
long[] updatedVisibleChunks = ChunkUtils.getVisibleChunks(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()));
|
||||
long[] lastVisibleChunks = ChunkUtils.getChunksInRange(new Position(16 * lastChunk.getChunkX(), 0, 16 * lastChunk.getChunkZ()), Main.CHUNK_VIEW_DISTANCE);
|
||||
long[] updatedVisibleChunks = ChunkUtils.getChunksInRange(new Position(16 * newChunk.getChunkX(), 0, 16 * newChunk.getChunkZ()), Main.CHUNK_VIEW_DISTANCE);
|
||||
int[] oldChunks = ArrayUtils.getDifferencesBetweenArray(lastVisibleChunks, updatedVisibleChunks);
|
||||
int[] newChunks = ArrayUtils.getDifferencesBetweenArray(updatedVisibleChunks, lastVisibleChunks);
|
||||
|
||||
@ -461,19 +458,18 @@ public class Player extends LivingEntity {
|
||||
int[] chunkPos = ChunkUtils.getChunkCoord(updatedVisibleChunks[index]);
|
||||
instance.loadOptionalChunk(chunkPos[0], chunkPos[1], chunk -> {
|
||||
if (chunk == null) {
|
||||
return; // Cannot load chunk (autoload not enabled)
|
||||
return; // Cannot load chunk (auto load not enabled)
|
||||
}
|
||||
instance.sendChunk(this, chunk);
|
||||
if (isFar && isLast)
|
||||
if (isFar && isLast) {
|
||||
updatePlayerPosition();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleport(Position position, Runnable callback) {
|
||||
if (instance == null)
|
||||
return;
|
||||
super.teleport(position, () -> {
|
||||
if (!instance.hasEnabledAutoChunkLoad() && isChunkUnloaded(position.getX(), position.getZ()))
|
||||
return;
|
||||
@ -585,7 +581,7 @@ public class Player extends LivingEntity {
|
||||
}
|
||||
|
||||
OpenWindowPacket openWindowPacket = new OpenWindowPacket();
|
||||
openWindowPacket.windowId = inventory.getUniqueId();
|
||||
openWindowPacket.windowId = inventory.getWindowId();
|
||||
openWindowPacket.windowType = inventory.getInventoryType().getWindowType();
|
||||
openWindowPacket.title = inventory.getTitle();
|
||||
playerConnection.sendPacket(openWindowPacket);
|
||||
@ -599,7 +595,7 @@ public class Player extends LivingEntity {
|
||||
if (openInventory == null) {
|
||||
closeWindowPacket.windowId = 0;
|
||||
} else {
|
||||
closeWindowPacket.windowId = openInventory.getUniqueId();
|
||||
closeWindowPacket.windowId = openInventory.getWindowId();
|
||||
openInventory.removeViewer(this);
|
||||
refreshOpenInventory(null);
|
||||
}
|
||||
@ -623,7 +619,7 @@ public class Player extends LivingEntity {
|
||||
return equipmentPacket;
|
||||
}
|
||||
|
||||
protected void updateViewPosition(Chunk chunk) {
|
||||
public void updateViewPosition(Chunk chunk) {
|
||||
UpdateViewPositionPacket updateViewPositionPacket = new UpdateViewPositionPacket(chunk);
|
||||
playerConnection.sendPacket(updateViewPositionPacket);
|
||||
}
|
||||
|
@ -51,16 +51,22 @@ public class BlockBatch implements BlockModifier {
|
||||
this.data.put(chunk, blockData);
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
public void flush(Runnable callback) {
|
||||
int counter = 0;
|
||||
for (Map.Entry<Chunk, List<BlockData>> entry : data.entrySet()) {
|
||||
counter++;
|
||||
Chunk chunk = entry.getKey();
|
||||
List<BlockData> dataList = entry.getValue();
|
||||
boolean isLast = counter == data.size();
|
||||
batchesPool.execute(() -> {
|
||||
synchronized (chunk) {
|
||||
for (BlockData data : dataList) {
|
||||
data.apply(chunk);
|
||||
}
|
||||
instance.sendChunkUpdate(chunk); // TODO partial chunk data
|
||||
chunk.refreshDataPacket();
|
||||
instance.sendChunkUpdate(chunk);
|
||||
if (isLast && callback != null)
|
||||
callback.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package fr.themode.minestom.instance;
|
||||
|
||||
import fr.themode.minestom.utils.BlockPosition;
|
||||
import fr.themode.minestom.utils.Position;
|
||||
|
||||
public interface BlockModifier {
|
||||
|
||||
@ -12,8 +13,16 @@ public interface BlockModifier {
|
||||
setBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), blockId);
|
||||
}
|
||||
|
||||
default void setBlock(Position position, short blockId) {
|
||||
setBlock(position.toBlockPosition(), blockId);
|
||||
}
|
||||
|
||||
default void setBlock(BlockPosition blockPosition, String blockId) {
|
||||
setBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), blockId);
|
||||
}
|
||||
|
||||
default void setBlock(Position position, String blockId) {
|
||||
setBlock(position.toBlockPosition(), blockId);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,8 +7,9 @@ import fr.themode.minestom.net.packet.server.play.ChunkDataPacket;
|
||||
import fr.themode.minestom.utils.PacketUtils;
|
||||
import fr.themode.minestom.utils.SerializerUtils;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
@ -34,7 +35,6 @@ public class Chunk {
|
||||
this.biome = biome;
|
||||
this.chunkX = chunkX;
|
||||
this.chunkZ = chunkZ;
|
||||
//refreshDataPacket(); // TODO remove
|
||||
}
|
||||
|
||||
protected void setBlock(byte x, byte y, byte z, short blockId) {
|
||||
@ -132,25 +132,6 @@ public class Chunk {
|
||||
return result;
|
||||
}
|
||||
|
||||
protected void loadFromFile(File file) throws IOException {
|
||||
System.out.println("LOAD FROM FILE");
|
||||
byte[] array = Files.readAllBytes(file.toPath());
|
||||
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(array));
|
||||
this.chunkX = stream.readInt();
|
||||
this.chunkZ = stream.readInt();
|
||||
System.out.println("chunk: " + chunkX + " : " + chunkZ);
|
||||
try {
|
||||
while (true) {
|
||||
int index = stream.readInt();
|
||||
boolean isCustomBlock = stream.readBoolean();
|
||||
short block = stream.readShort();
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
System.out.println("END");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public ChunkDataPacket getFreshFullDataPacket() {
|
||||
ChunkDataPacket fullDataPacket = new ChunkDataPacket();
|
||||
fullDataPacket.chunk = this;
|
||||
|
@ -8,6 +8,9 @@ import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Use chunk coordinate (0-16) instead of world's
|
||||
*/
|
||||
public class ChunkBatch implements BlockModifier {
|
||||
|
||||
private static volatile ExecutorService batchesPool = Executors.newFixedThreadPool(Main.THREAD_COUNT_CHUNK_BATCH);
|
||||
@ -50,9 +53,9 @@ 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
|
||||
|
||||
chunk.refreshDataPacket();
|
||||
instance.sendChunkUpdate(chunk);
|
||||
if (callback != null)
|
||||
callback.accept(chunk);
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
package fr.themode.minestom.instance;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.Main;
|
||||
import fr.themode.minestom.entity.Entity;
|
||||
import fr.themode.minestom.entity.EntityCreature;
|
||||
import fr.themode.minestom.entity.ObjectEntity;
|
||||
@ -152,16 +153,20 @@ public abstract class Instance implements BlockModifier {
|
||||
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)); // 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));
|
||||
|
||||
// Send player all visible entities
|
||||
long[] visibleChunksEntity = ChunkUtils.getChunksInRange(entity.getPosition(), Main.ENTITY_VIEW_DISTANCE);
|
||||
for (long chunkIndex : visibleChunksEntity) {
|
||||
getEntitiesInChunk(chunkIndex).forEach(ent -> {
|
||||
ent.addViewer(player);
|
||||
if (ent instanceof Player) {
|
||||
player.addViewer((Player) ent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Chunk chunk = getChunkAt(entity.getPosition());
|
||||
|
@ -207,19 +207,15 @@ public class InstanceContainer extends Instance {
|
||||
|
||||
@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();
|
||||
PacketWriter.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> {
|
||||
buffer.getData().retain(1).markReaderIndex();
|
||||
player.getPlayerConnection().sendUnencodedPacket(buffer);
|
||||
buffer.getData().resetReaderIndex();
|
||||
});
|
||||
// TODO use cached chunk data
|
||||
/*chunkData.getData().retain(1).markReaderIndex();
|
||||
player.getPlayerConnection().sendUnencodedPacket(chunkData);
|
||||
chunkData.getData().resetReaderIndex();
|
||||
}
|
||||
chunkData.getData().resetReaderIndex();*/
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -50,7 +50,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
return title;
|
||||
}
|
||||
|
||||
public int getUniqueId() {
|
||||
public int getWindowId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
return Arrays.copyOf(itemStacks, itemStacks.length);
|
||||
}
|
||||
|
||||
public void updateItems() {
|
||||
public void update() {
|
||||
WindowItemsPacket windowItemsPacket = getWindowItemsPacket();
|
||||
getViewers().forEach(p -> p.getPlayerConnection().sendPacket(windowItemsPacket));
|
||||
}
|
||||
@ -117,7 +117,7 @@ public class Inventory implements InventoryModifier, InventoryClickHandler, View
|
||||
|
||||
private WindowItemsPacket getWindowItemsPacket() {
|
||||
WindowItemsPacket windowItemsPacket = new WindowItemsPacket();
|
||||
windowItemsPacket.windowId = getUniqueId();
|
||||
windowItemsPacket.windowId = getWindowId();
|
||||
windowItemsPacket.count = (short) itemStacks.length;
|
||||
windowItemsPacket.items = itemStacks;
|
||||
return windowItemsPacket;
|
||||
|
@ -0,0 +1,40 @@
|
||||
package fr.themode.minestom.inventory;
|
||||
|
||||
public enum InventoryProperty {
|
||||
|
||||
FURNACE_FIRE_ICON((short) 0),
|
||||
FURNACE_MAXIMUM_FUEL_BURN_TIME((short) 1),
|
||||
FURNACE_PROGRESS_ARROW((short) 2),
|
||||
FURNACE_MAXIMUM_PROGRESS((short) 3),
|
||||
|
||||
ENCHANTMENT_TABLE_LEVEL_REQUIREMENT_TOP((short) 0),
|
||||
ENCHANTMENT_TABLE_LEVEL_REQUIREMENT_MIDDLE((short) 1),
|
||||
ENCHANTMENT_TABLE_LEVEL_REQUIREMENT_BOTTOM((short) 2),
|
||||
ENCHANTMENT_TABLE_SEED((short) 3),
|
||||
ENCHANTMENT_TABLE_ENCH_ID_TOP((short) 4),
|
||||
ENCHANTMENT_TABLE_ENCH_ID_MIDDLE((short) 5),
|
||||
ENCHANTMENT_TABLE_ENCH_ID_BOTTOM((short) 6),
|
||||
ENCHANTMENT_TABLE_ENCH_LEVEL_TOP((short) 7),
|
||||
ENCHANTMENT_TABLE_ENCH_LEVEL_MIDDLE((short) 8),
|
||||
ENCHANTMENT_TABLE_ENCH_LEVEL_BOTTOM((short) 9),
|
||||
|
||||
BEACON_POWER_LEVEL((short) 0),
|
||||
BEACON_FIRST_POTION((short) 1),
|
||||
BEACON_SECOND_POTION((short) 2),
|
||||
|
||||
ANVIL_REPAIR_COST((short) 0),
|
||||
|
||||
BREWING_STAND_BREW_TIME((short) 0),
|
||||
BREWING_STAND_FUEL_TIME((short) 1);
|
||||
|
||||
|
||||
private short property;
|
||||
|
||||
InventoryProperty(short property) {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
public short getProperty() {
|
||||
return property;
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package fr.themode.minestom.inventory.type;
|
||||
|
||||
import fr.themode.minestom.inventory.Inventory;
|
||||
import fr.themode.minestom.inventory.InventoryType;
|
||||
import fr.themode.minestom.net.packet.server.play.TradeListPacket;
|
||||
import fr.themode.minestom.utils.ArrayUtils;
|
||||
|
||||
public class VillagerInventory extends Inventory {
|
||||
|
||||
protected TradeListPacket tradeListPacket;
|
||||
|
||||
public VillagerInventory(String title) {
|
||||
super(InventoryType.MERCHANT, title);
|
||||
setupPacket();
|
||||
}
|
||||
|
||||
public TradeListPacket.Trade[] getTrades() {
|
||||
return tradeListPacket.trades;
|
||||
}
|
||||
|
||||
public void addTrade(TradeListPacket.Trade trade) {
|
||||
TradeListPacket.Trade[] oldTrades = getTrades();
|
||||
int length = oldTrades.length + 1;
|
||||
TradeListPacket.Trade[] trades = new TradeListPacket.Trade[length];
|
||||
System.arraycopy(oldTrades, 0, trades, 0, oldTrades.length);
|
||||
trades[length] = trade;
|
||||
this.tradeListPacket.trades = trades;
|
||||
update();
|
||||
}
|
||||
|
||||
public void removeTrade(int index) {
|
||||
TradeListPacket.Trade[] oldTrades = getTrades();
|
||||
int length = oldTrades.length - 1;
|
||||
TradeListPacket.Trade[] trades = new TradeListPacket.Trade[length];
|
||||
ArrayUtils.removeElement(trades, index);
|
||||
this.tradeListPacket.trades = trades;
|
||||
update();
|
||||
}
|
||||
|
||||
public int getVillagerLevel() {
|
||||
return tradeListPacket.villagerLevel;
|
||||
}
|
||||
|
||||
public void setVillagerLevel(int level) {
|
||||
this.tradeListPacket.villagerLevel = level;
|
||||
update();
|
||||
}
|
||||
|
||||
public int getExperience() {
|
||||
return tradeListPacket.experience;
|
||||
}
|
||||
|
||||
public void setExperience(int experience) {
|
||||
this.tradeListPacket.experience = experience;
|
||||
update();
|
||||
}
|
||||
|
||||
public boolean isRegularVillager() {
|
||||
return tradeListPacket.regularVillager;
|
||||
}
|
||||
|
||||
public void setRegularVillager(boolean regularVillager) {
|
||||
this.tradeListPacket.regularVillager = regularVillager;
|
||||
update();
|
||||
}
|
||||
|
||||
public boolean canRestock() {
|
||||
return tradeListPacket.canRestock;
|
||||
}
|
||||
|
||||
public void setCanRestock(boolean canRestock) {
|
||||
this.tradeListPacket.canRestock = canRestock;
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
super.update();
|
||||
sendPacketToViewers(tradeListPacket); // Refresh window
|
||||
}
|
||||
|
||||
private void setupPacket() {
|
||||
this.tradeListPacket = new TradeListPacket();
|
||||
this.tradeListPacket.windowId = getWindowId();
|
||||
this.tradeListPacket.trades = new TradeListPacket.Trade[0];
|
||||
this.tradeListPacket.villagerLevel = 0;
|
||||
this.tradeListPacket.experience = 0;
|
||||
this.tradeListPacket.regularVillager = false;
|
||||
this.tradeListPacket.canRestock = false;
|
||||
}
|
||||
}
|
@ -7,8 +7,9 @@ import fr.themode.minestom.net.packet.client.play.ClientChatMessagePacket;
|
||||
public class ChatMessageListener {
|
||||
|
||||
public static void listener(ClientChatMessagePacket packet, Player player) {
|
||||
// TODO commands check
|
||||
Main.getConnectionManager().getOnlinePlayers().forEach(p -> p.sendMessage(String.format("<%s> %s", player.getUsername(), packet.message)));
|
||||
String message = packet.message;
|
||||
Main.getConnectionManager().getOnlinePlayers().forEach(p -> p.sendMessage(String.format("<%s> %s", player.getUsername(), message)));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,11 +4,12 @@ import fr.themode.minestom.entity.Player;
|
||||
import fr.themode.minestom.net.player.PlayerConnection;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
|
||||
public class ConnectionManager {
|
||||
|
||||
private volatile Set<Player> players = new HashSet<>();
|
||||
private volatile Map<PlayerConnection, Player> connectionPlayerMap = new HashMap<>();
|
||||
private Set<Player> players = new CopyOnWriteArraySet<>();
|
||||
private Map<PlayerConnection, Player> connectionPlayerMap = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
public Player getPlayer(PlayerConnection connection) {
|
||||
return connectionPlayerMap.get(connection);
|
||||
|
@ -15,7 +15,10 @@ public class ClientPacketsHandler {
|
||||
}
|
||||
|
||||
public ClientPacket getPacketInstance(int id) {
|
||||
return constructorAccessMap.get(id).newInstance();
|
||||
ClientPacket packet = constructorAccessMap.get(id).newInstance();
|
||||
if (packet == null)
|
||||
System.err.println("Packet id 0x" + Integer.toHexString(id) + " isn't registered!");
|
||||
return packet;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,31 @@
|
||||
package fr.themode.minestom.net.packet.server.play;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||
|
||||
public class ExplosionPacket implements ServerPacket {
|
||||
|
||||
public float x, y, z;
|
||||
public float radius; // UNUSED
|
||||
public byte[] records;
|
||||
public float playerMotionX, playerMotionY, playerMotionZ;
|
||||
|
||||
@Override
|
||||
public void write(Buffer buffer) {
|
||||
buffer.putFloat(x);
|
||||
buffer.putFloat(y);
|
||||
buffer.putFloat(z);
|
||||
buffer.putFloat(radius);
|
||||
buffer.putInt(records.length);
|
||||
for (byte record : records)
|
||||
buffer.putByte(record);
|
||||
buffer.putFloat(playerMotionX);
|
||||
buffer.putFloat(playerMotionY);
|
||||
buffer.putFloat(playerMotionZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return 0x1C;
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package fr.themode.minestom.net.packet.server.play;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.item.ItemStack;
|
||||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||
import fr.themode.minestom.utils.Utils;
|
||||
|
||||
public class TradeListPacket implements ServerPacket {
|
||||
|
||||
public int windowId;
|
||||
public Trade[] trades;
|
||||
public int villagerLevel;
|
||||
public int experience;
|
||||
public boolean regularVillager;
|
||||
public boolean canRestock;
|
||||
|
||||
@Override
|
||||
public void write(Buffer buffer) {
|
||||
Utils.writeVarInt(buffer, windowId);
|
||||
buffer.putByte((byte) trades.length);
|
||||
for (Trade trade : trades) {
|
||||
trade.write(buffer);
|
||||
}
|
||||
Utils.writeVarInt(buffer, villagerLevel);
|
||||
Utils.writeVarInt(buffer, experience);
|
||||
buffer.putBoolean(regularVillager);
|
||||
buffer.putBoolean(canRestock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return 0x27;
|
||||
}
|
||||
|
||||
public static class Trade {
|
||||
|
||||
public ItemStack inputItem1;
|
||||
public ItemStack result;
|
||||
public ItemStack inputItem2;
|
||||
public boolean tradeDisabled;
|
||||
public int tradeUsesNumber;
|
||||
public int maxTradeUsesNumber;
|
||||
public int exp;
|
||||
public int specialPrice;
|
||||
public float priceMultiplier;
|
||||
public int demand;
|
||||
|
||||
|
||||
private void write(Buffer buffer) {
|
||||
Utils.writeItemStack(buffer, inputItem1);
|
||||
Utils.writeItemStack(buffer, result);
|
||||
buffer.putBoolean(inputItem2 != null);
|
||||
if (inputItem2 != null)
|
||||
Utils.writeItemStack(buffer, inputItem2);
|
||||
buffer.putBoolean(tradeDisabled);
|
||||
buffer.putInt(tradeUsesNumber);
|
||||
buffer.putInt(maxTradeUsesNumber);
|
||||
buffer.putInt(exp);
|
||||
buffer.putInt(specialPrice);
|
||||
buffer.putFloat(priceMultiplier);
|
||||
buffer.putInt(demand);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package fr.themode.minestom.net.packet.server.play;
|
||||
|
||||
import fr.adamaq01.ozao.net.Buffer;
|
||||
import fr.themode.minestom.net.packet.server.ServerPacket;
|
||||
|
||||
public class WindowPropertyPacket implements ServerPacket {
|
||||
|
||||
public byte windowId;
|
||||
public short property;
|
||||
public short value;
|
||||
|
||||
@Override
|
||||
public void write(Buffer buffer) {
|
||||
buffer.putByte(windowId);
|
||||
buffer.putShort(property);
|
||||
buffer.putShort(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return 0x15;
|
||||
}
|
||||
}
|
@ -17,6 +17,10 @@ public class ArrayUtils {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void removeElement(Object[] arr, int index) {
|
||||
System.arraycopy(arr, index + 1, arr, index, arr.length - 1 - index);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param a
|
||||
* @param b
|
||||
|
@ -1,7 +1,5 @@
|
||||
package fr.themode.minestom.utils;
|
||||
|
||||
import fr.themode.minestom.Main;
|
||||
|
||||
public class ChunkUtils {
|
||||
|
||||
public static long getChunkIndex(int chunkX, int chunkZ) {
|
||||
@ -14,12 +12,11 @@ public class ChunkUtils {
|
||||
return new int[]{chunkX, chunkZ};
|
||||
}
|
||||
|
||||
public static long[] getVisibleChunks(final Position position) {
|
||||
final int viewDistance = Main.CHUNK_VIEW_DISTANCE;
|
||||
public static long[] getChunksInRange(final Position position, int range) {
|
||||
|
||||
long[] visibleChunks = new long[MathUtils.square(viewDistance + 1)];
|
||||
final int startLoop = -(viewDistance / 2);
|
||||
final int endLoop = viewDistance / 2 + 1;
|
||||
long[] visibleChunks = new long[MathUtils.square(range + 1)];
|
||||
final int startLoop = -(range / 2);
|
||||
final int endLoop = range / 2 + 1;
|
||||
int counter = 0;
|
||||
for (int x = startLoop; x < endLoop; x++) {
|
||||
for (int z = startLoop; z < endLoop; z++) {
|
||||
|
29
src/main/java/fr/themode/minestom/utils/EntityUtils.java
Normal file
29
src/main/java/fr/themode/minestom/utils/EntityUtils.java
Normal file
@ -0,0 +1,29 @@
|
||||
package fr.themode.minestom.utils;
|
||||
|
||||
import fr.themode.minestom.Main;
|
||||
import fr.themode.minestom.entity.Entity;
|
||||
import fr.themode.minestom.instance.Chunk;
|
||||
|
||||
public class EntityUtils {
|
||||
|
||||
public static boolean areVisible(Entity ent1, Entity ent2) {
|
||||
if (ent1.getInstance() == null || ent2.getInstance() == null)
|
||||
return false;
|
||||
if (!ent1.getInstance().equals(ent2.getInstance()))
|
||||
return false;
|
||||
|
||||
Chunk chunk = ent1.getInstance().getChunkAt(ent1.getPosition());
|
||||
|
||||
long[] visibleChunksEntity = ChunkUtils.getChunksInRange(ent2.getPosition(), Main.ENTITY_VIEW_DISTANCE);
|
||||
for (long visibleChunk : visibleChunksEntity) {
|
||||
int[] chunkPos = ChunkUtils.getChunkCoord(visibleChunk);
|
||||
int chunkX = chunkPos[0];
|
||||
int chunkZ = chunkPos[1];
|
||||
if (chunk.getChunkX() == chunkX && chunk.getChunkZ() == chunkZ)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -132,6 +132,10 @@ public class Position {
|
||||
this.pitch = pitch;
|
||||
}
|
||||
|
||||
public BlockPosition toBlockPosition() {
|
||||
return new BlockPosition((int) getX(), (int) getY(), (int) getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Position[" + x + ":" + y + ":" + z + "] (" + yaw + "/" + pitch + ")";
|
||||
|
Loading…
Reference in New Issue
Block a user