This commit is contained in:
TheMode 2019-08-27 05:23:25 +02:00
parent 4b1fac6cd4
commit 0a732034c2
25 changed files with 467 additions and 122 deletions

View File

@ -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);
}

View File

@ -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);
}
}
});
}
}

View File

@ -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)
*/
}

View File

@ -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) {

View File

@ -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];

View File

@ -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);
}

View File

@ -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();
}
});
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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);
});

View File

@ -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());

View File

@ -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

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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)));
}
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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++) {

View 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;
}
}

View File

@ -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 + ")";