This commit is contained in:
TheMode 2019-08-19 17:04:19 +02:00
parent 4263a3965b
commit bdde4af581
36 changed files with 1176 additions and 118 deletions

View File

@ -0,0 +1,19 @@
package fr.themode.minestom;
import fr.themode.minestom.entity.Player;
import java.util.Set;
public interface Viewable {
void addViewer(Player player);
void removeViewer(Player player);
Set<Player> getViewers();
default boolean isViewer(Player player) {
return getViewers().contains(player);
}
}

View File

@ -0,0 +1,13 @@
package fr.themode.minestom.bossbar;
public enum BarColor {
PINK,
BLUE,
RED,
GREEN,
YELLOW,
PURPLE,
WHITE;
}

View File

@ -0,0 +1,11 @@
package fr.themode.minestom.bossbar;
public enum BarDivision {
SOLID,
SEGMENT_6,
SEGMENT_10,
SEGMENT_12,
SEGMENT_20;
}

View File

@ -0,0 +1,138 @@
package fr.themode.minestom.bossbar;
import fr.themode.minestom.Viewable;
import fr.themode.minestom.chat.Chat;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.net.packet.server.play.BossBarPacket;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
public class BossBar implements Viewable {
private UUID uuid = UUID.randomUUID();
private Set<Player> viewers = new CopyOnWriteArraySet<>();
private String title;
private float progress;
private BarColor color;
private BarDivision division;
private byte flags;
public BossBar(String title, BarColor color, BarDivision division) {
this.title = Chat.rawText(title);
this.color = color;
this.division = division;
}
@Override
public void addViewer(Player player) {
this.viewers.add(player);
player.refreshAddBossbar(this);
addToPlayer(player);
}
@Override
public void removeViewer(Player player) {
this.viewers.remove(player);
player.refreshRemoveBossbar(this);
removeToPlayer(player);
}
@Override
public Set<Player> getViewers() {
return Collections.unmodifiableSet(viewers);
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = Chat.rawText(title);
}
public float getProgress() {
return progress;
}
public void setProgress(float progress) {
this.progress = progress;
updateProgress();
}
public BarColor getColor() {
return color;
}
public void setColor(BarColor color) {
this.color = color;
updateStyle();
}
public BarDivision getDivision() {
return division;
}
public void setDivision(BarDivision division) {
this.division = division;
updateStyle();
}
public void remove() {
BossBarPacket bossBarPacket = new BossBarPacket();
bossBarPacket.uuid = uuid;
bossBarPacket.action = BossBarPacket.Action.REMOVE;
sendPacket(bossBarPacket);
// TODO remove bar from player class
}
private void addToPlayer(Player player) {
BossBarPacket bossBarPacket = new BossBarPacket();
bossBarPacket.uuid = uuid;
bossBarPacket.action = BossBarPacket.Action.ADD;
bossBarPacket.title = title;
bossBarPacket.health = progress;
bossBarPacket.color = color;
bossBarPacket.division = division;
bossBarPacket.flags = flags;
player.getPlayerConnection().sendPacket(bossBarPacket);
}
private void removeToPlayer(Player player) {
BossBarPacket bossBarPacket = new BossBarPacket();
bossBarPacket.uuid = uuid;
bossBarPacket.action = BossBarPacket.Action.REMOVE;
player.getPlayerConnection().sendPacket(bossBarPacket);
}
private void updateTitle() {
BossBarPacket bossBarPacket = new BossBarPacket();
bossBarPacket.uuid = uuid;
bossBarPacket.action = BossBarPacket.Action.UPDATE_TITLE;
bossBarPacket.title = title;
sendPacket(bossBarPacket);
}
private void updateProgress() {
BossBarPacket bossBarPacket = new BossBarPacket();
bossBarPacket.uuid = uuid;
bossBarPacket.action = BossBarPacket.Action.UPDATE_HEALTH;
bossBarPacket.health = progress;
sendPacket(bossBarPacket);
}
private void updateStyle() {
BossBarPacket bossBarPacket = new BossBarPacket();
bossBarPacket.uuid = uuid;
bossBarPacket.action = BossBarPacket.Action.UPDATE_STYLE;
bossBarPacket.color = color;
sendPacket(bossBarPacket);
}
private void sendPacket(BossBarPacket bossBarPacket) {
getViewers().forEach(player -> player.getPlayerConnection().sendPacket(bossBarPacket));
}
}

View File

@ -0,0 +1,8 @@
package fr.themode.minestom.chat;
public class Chat {
public static String rawText(String text) {
return "{\"text\": \"" + text + "\"}";
}
}

View File

@ -1,36 +1,81 @@
package fr.themode.minestom.entity;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.instance.Chunk;
import fr.themode.minestom.instance.Instance;
import fr.themode.minestom.utils.Utils;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
public class Entity {
public abstract class Entity {
private static AtomicInteger lastEntityId = new AtomicInteger();
// Metadata
protected static final byte METADATA_BYTE = 0;
protected static final byte METADATA_VARINT = 1;
protected static final byte METADATA_FLOAT = 2;
protected static final byte METADATA_STRING = 3;
protected static final byte METADATA_CHAT = 4;
protected static final byte METADATA_OPTCHAT = 5;
protected static final byte METADATA_SLOT = 6;
protected static final byte METADATA_BOOLEAN = 7;
protected Instance instance;
protected double lastX, lastY, lastZ;
protected double x, y, z;
protected float yaw, pitch;
private int id;
// Metadata
protected boolean onFire;
protected UUID uuid;
private boolean isActive; // False if entity has only been instanced without being added somewhere
protected boolean crouched;
private boolean shouldRemove;
protected boolean UNUSED_METADATA;
protected boolean sprinting;
protected boolean swimming;
protected boolean invisible;
protected boolean glowing;
protected boolean usingElytra;
protected int air = 300;
protected String customName = "";
protected boolean customNameVisible;
protected boolean silent;
protected boolean noGravity;
protected Pose pose = Pose.STANDING;
private int entityType;
private long lastUpdate;
public Entity() {
public Entity(int entityType) {
this.id = generateId();
this.entityType = entityType;
this.uuid = UUID.randomUUID();
}
private static int generateId() {
return lastEntityId.incrementAndGet();
}
public abstract void update();
public void tick() {
if (shouldUpdate()) {
update();
this.lastUpdate = System.currentTimeMillis();
}
}
public int getEntityId() {
return id;
}
public int getEntityType() {
return entityType;
}
public UUID getUuid() {
return uuid;
}
@ -56,6 +101,10 @@ public class Entity {
instance.addEntity(this);
}
public float getDistance(Entity entity) {
return (float) Math.sqrt(Math.pow(entity.getX() - getX(), 2) + Math.pow(entity.getY() - getY(), 2) + Math.pow(entity.getZ() - getZ(), 2));
}
public void refreshPosition(double x, double y, double z) {
this.lastX = this.x;
this.lastY = this.y;
@ -91,10 +140,101 @@ public class Entity {
return z;
}
public float getYaw() {
return yaw;
}
public float getPitch() {
return pitch;
}
public void remove() {
this.shouldRemove = true;
}
public Buffer getMetadataBuffer() {
Buffer buffer = Buffer.create();
fillMetadataIndex(buffer, 0);
/*Utils.writeVarInt(buffer, 0);
Utils.writeString(buffer, customName);
buffer.putBoolean(silent);
buffer.putBoolean(noGravity);
Utils.writeVarInt(buffer, pose.ordinal());*/
// Chicken test
/*buffer.putByte((byte) 0); // Hand states
buffer.putFloat(10f); // Health
Utils.writeVarInt(buffer, 0); // Potion effect color
buffer.putBoolean(false); // Potion effect ambient
Utils.writeVarInt(buffer, 0); // Arrow count in entity
buffer.putByte((byte) 0); // (Insentient)
buffer.putBoolean(false); // baby (Ageable)*/
return buffer;
}
private void fillMetadataIndex(Buffer buffer, int index) {
switch (index) {
case 0:
fillStateMetadata(buffer);
break;
case 1:
fillAirTickMetaData(buffer);
break;
case 2:
fillCustomNameMetaData(buffer);
break;
}
}
private void fillStateMetadata(Buffer buffer) {
buffer.putByte((byte) 0);
buffer.putByte(METADATA_BYTE);
byte index0 = 0;
if (onFire)
index0 += 1;
if (crouched)
index0 += 2;
if (UNUSED_METADATA)
index0 += 4;
if (sprinting)
index0 += 8;
if (swimming)
index0 += 16;
if (invisible)
index0 += 32;
if (glowing)
index0 += 64;
if (usingElytra)
index0 += 128;
buffer.putByte(index0);
}
private void fillAirTickMetaData(Buffer buffer) {
buffer.putByte((byte) 1);
buffer.putByte(METADATA_VARINT);
Utils.writeVarInt(buffer, air);
}
private void fillCustomNameMetaData(Buffer buffer) {
buffer.putByte((byte) 2);
buffer.putByte(METADATA_CHAT);
Utils.writeString(buffer, customName);
}
private boolean shouldUpdate() {
return (float) (System.currentTimeMillis() - lastUpdate) >= 50f * 0.9f; // Margin of error
}
public enum Pose {
STANDING,
FALL_FLYING,
SLEEPING,
SWIMMING,
SPIN_ATTACK,
SNEAKING,
DYING;
}
protected boolean shouldRemove() {
return shouldRemove;
}

View File

@ -1,25 +1,20 @@
package fr.themode.minestom.entity;
import fr.themode.minestom.Viewable;
import fr.themode.minestom.net.packet.server.ServerPacket;
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.EntityTeleportPacket;
import fr.themode.minestom.net.packet.server.play.SpawnMobPacket;
import fr.themode.minestom.net.packet.server.play.*;
import fr.themode.minestom.net.player.PlayerConnection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
public abstract class EntityCreature extends LivingEntity {
public abstract class EntityCreature extends LivingEntity implements Viewable {
private Set<Player> viewers = Collections.synchronizedSet(new HashSet<>());
private int entityType;
private Set<Player> viewers = new CopyOnWriteArraySet<>();
public EntityCreature(int entityType) {
super();
this.entityType = entityType;
super(entityType);
}
public void move(double x, double y, double z) {
@ -56,6 +51,7 @@ public abstract class EntityCreature extends LivingEntity {
sendPacketToViewers(entityTeleportPacket);
}
@Override
public void addViewer(Player player) {
this.viewers.add(player);
PlayerConnection playerConnection = player.getPlayerConnection();
@ -72,10 +68,15 @@ public abstract class EntityCreature extends LivingEntity {
spawnMobPacket.yaw = getYaw();
spawnMobPacket.pitch = getPitch();
spawnMobPacket.headPitch = 0;
EntityMetaDataPacket entityMetaDataPacket = new EntityMetaDataPacket();
entityMetaDataPacket.entityId = getEntityId();
entityMetaDataPacket.data = getMetadataBuffer();
playerConnection.sendPacket(entityPacket);
playerConnection.sendPacket(spawnMobPacket);
playerConnection.sendPacket(entityMetaDataPacket);
}
@Override
public void removeViewer(Player player) {
synchronized (viewers) {
if (!viewers.contains(player))
@ -85,15 +86,12 @@ public abstract class EntityCreature extends LivingEntity {
}
}
protected void sendPacketToViewers(ServerPacket packet) {
getViewers().forEach(player -> player.getPlayerConnection().sendPacket(packet));
}
@Override
public Set<Player> getViewers() {
return Collections.unmodifiableSet(viewers);
}
public int getEntityType() {
return entityType;
protected void sendPacketToViewers(ServerPacket packet) {
getViewers().forEach(player -> player.getPlayerConnection().sendPacket(packet));
}
}

View File

@ -1,9 +1,11 @@
package fr.themode.minestom.entity;
import fr.themode.minestom.Main;
import fr.themode.minestom.instance.Chunk;
import fr.themode.minestom.instance.Instance;
import fr.themode.minestom.instance.InstanceManager;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -11,43 +13,98 @@ public class EntityManager {
private static InstanceManager instanceManager = Main.getInstanceManager();
private ExecutorService objectsPool = Executors.newFixedThreadPool(2);
private ExecutorService creaturesPool = Executors.newFixedThreadPool(2);
private ExecutorService playersPool = Executors.newFixedThreadPool(2);
public void update() {
for (Instance instance : instanceManager.getInstances()) {
// TODO loop chunks and entities on it instead of individual (to have more non-blocking operation)
// Creatures
for (EntityCreature creature : instance.getCreatures()) {
creaturesPool.submit(() -> {
boolean shouldRemove = creature.shouldRemove();
if (!shouldRemove) {
creature.update();
}
if (creature.shouldRemove()) {
instance.removeEntity(creature);
}
});
}
// Players
for (Player player : instance.getPlayers()) {
playersPool.submit(() -> {
boolean shouldRemove = player.shouldRemove();
if (!shouldRemove) {
player.update();
}
if (player.shouldRemove()) {
instance.removeEntity(player);
}
});
}
testTick2(instance);
}
}
private void testTick2(Instance instance) {
for (Chunk chunk : instance.getChunks()) {
Set<ObjectEntity> objects = chunk.getObjectEntities();
Set<EntityCreature> creatures = chunk.getCreatures();
Set<Player> players = chunk.getPlayers();
if (!objects.isEmpty()) {
objectsPool.submit(() -> {
for (ObjectEntity objectEntity : objects) {
boolean shouldRemove = objectEntity.shouldRemove();
if (!shouldRemove) {
objectEntity.tick();
}
if (objectEntity.shouldRemove()) {
instance.removeEntity(objectEntity);
}
}
});
}
if (!creatures.isEmpty()) {
creaturesPool.submit(() -> {
for (EntityCreature creature : creatures) {
boolean shouldRemove = creature.shouldRemove();
if (!shouldRemove) {
creature.tick();
}
if (creature.shouldRemove()) {
instance.removeEntity(creature);
}
}
});
}
if (!players.isEmpty()) {
playersPool.submit(() -> {
for (Player player : players) {
boolean shouldRemove = player.shouldRemove();
if (!shouldRemove) {
player.tick();
}
if (player.shouldRemove()) {
instance.removeEntity(player);
}
}
});
}
}
}
private void testTick(Instance instance) {
// Creatures
for (EntityCreature creature : instance.getCreatures()) {
creaturesPool.submit(() -> {
boolean shouldRemove = creature.shouldRemove();
if (!shouldRemove) {
creature.tick();
}
if (creature.shouldRemove()) {
instance.removeEntity(creature);
}
});
}
// Players
for (Player player : instance.getPlayers()) {
playersPool.submit(() -> {
boolean shouldRemove = player.shouldRemove();
if (!shouldRemove) {
player.tick();
}
if (player.shouldRemove()) {
instance.removeEntity(player);
}
});
}
}
}

View File

@ -0,0 +1,47 @@
package fr.themode.minestom.entity;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.utils.Utils;
public class ItemEntity extends ObjectEntity {
private ItemStack itemStack;
private boolean pickable = true;
public ItemEntity(ItemStack itemStack) {
super(34);
this.itemStack = itemStack;
}
@Override
public void update() {
}
@Override
public Buffer getMetadataBuffer() {
Buffer buffer = super.getMetadataBuffer();
buffer.putByte((byte) 7);
buffer.putByte(METADATA_SLOT);
Utils.writeItemStack(buffer, itemStack);
return buffer;
}
@Override
public int getData() {
return 1;
}
public ItemStack getItemStack() {
return itemStack;
}
public boolean isPickable() {
return pickable;
}
public void setPickable(boolean pickable) {
this.pickable = pickable;
}
}

View File

@ -2,25 +2,14 @@ package fr.themode.minestom.entity;
public abstract class LivingEntity extends Entity {
protected float yaw, pitch;
protected boolean onGround;
public LivingEntity() {
super();
public LivingEntity(int entityType) {
super(entityType);
}
public abstract void update();
public boolean chunkTest(double x, double z) {
return getInstance().getChunk((int) Math.floor(x / 16), (int) Math.floor(z / 16)) == null;
}
public float getYaw() {
return yaw;
}
public float getPitch() {
return pitch;
}
}

View File

@ -0,0 +1,53 @@
package fr.themode.minestom.entity;
import fr.themode.minestom.Viewable;
import fr.themode.minestom.net.packet.server.play.EntityMetaDataPacket;
import fr.themode.minestom.net.packet.server.play.SpawnObjectPacket;
import fr.themode.minestom.net.player.PlayerConnection;
import java.util.Collections;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
public abstract class ObjectEntity extends Entity implements Viewable {
private Set<Player> viewers = new CopyOnWriteArraySet<>();
public ObjectEntity(int entityType) {
super(entityType);
}
public abstract int getData();
@Override
public void addViewer(Player player) {
this.viewers.add(player);
PlayerConnection playerConnection = player.getPlayerConnection();
SpawnObjectPacket spawnObjectPacket = new SpawnObjectPacket();
spawnObjectPacket.entityId = getEntityId();
spawnObjectPacket.uuid = getUuid();
spawnObjectPacket.type = getEntityType();
spawnObjectPacket.x = getX();
spawnObjectPacket.y = getY();
spawnObjectPacket.z = getZ();
spawnObjectPacket.yaw = getYaw();
spawnObjectPacket.pitch = getPitch();
spawnObjectPacket.data = getData();
EntityMetaDataPacket entityMetaDataPacket = new EntityMetaDataPacket();
entityMetaDataPacket.entityId = getEntityId();
entityMetaDataPacket.data = getMetadataBuffer();
playerConnection.sendPacket(spawnObjectPacket);
playerConnection.sendPacket(entityMetaDataPacket);
}
@Override
public void removeViewer(Player player) {
this.viewers.remove(player);
}
@Override
public Set<Player> getViewers() {
return Collections.unmodifiableSet(viewers);
}
}

View File

@ -1,21 +1,24 @@
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.instance.CustomBlock;
import fr.themode.minestom.inventory.Inventory;
import fr.themode.minestom.inventory.PlayerInventory;
import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.net.packet.server.play.*;
import fr.themode.minestom.net.player.PlayerConnection;
import fr.themode.minestom.utils.GroupedCollections;
import fr.themode.minestom.utils.Position;
import java.util.Collections;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
public class Player extends LivingEntity {
private boolean isSneaking;
private boolean isSprinting;
private long lastKeepAlive;
private String username;
@ -30,8 +33,11 @@ public class Player extends LivingEntity {
private Position targetBlockPosition;
private long targetBlockTime;
private Set<BossBar> bossBars = new CopyOnWriteArraySet<>();
// TODO set proper UUID
public Player(UUID uuid, String username, PlayerConnection playerConnection) {
super(93); // TODO correct ?
this.uuid = uuid;
this.username = username;
this.playerConnection = playerConnection;
@ -51,11 +57,25 @@ public class Player extends LivingEntity {
sendBlockBreakAnimation(targetBlockPosition, stage);// TODO send to all near players
if (stage > 9) {
instance.setBlock(targetBlockPosition.getX(), targetBlockPosition.getY(), targetBlockPosition.getZ(), (short) 0);
testParticle(targetBlockPosition.getX() + 0.5f, targetBlockPosition.getY(), targetBlockPosition.getZ() + 0.5f);
testParticle(targetBlockPosition.getX() + 0.5f, targetBlockPosition.getY(), targetBlockPosition.getZ() + 0.5f, targetCustomBlock.getType());
resetTargetBlock();
}
}
// Item pickup
if (instance != null) {
GroupedCollections<ObjectEntity> objectEntities = instance.getObjectEntities();
for (ObjectEntity objectEntity : objectEntities) {
if (objectEntity instanceof ItemEntity) {
float distance = getDistance(objectEntity);
if (distance <= 1) { // FIXME set correct value
getInventory().addItemStack(((ItemEntity) objectEntity).getItemStack());
objectEntity.remove();
}
}
}
}
// Multiplayer sync
EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket();
@ -81,7 +101,7 @@ public class Player extends LivingEntity {
playerConnection.sendPacket(breakAnimationPacket);
}
private void testParticle(float x, float y, float z) {
private void testParticle(float x, float y, float z, int blockId) {
ParticlePacket particlePacket = new ParticlePacket();
particlePacket.particleId = 3; // Block particle
particlePacket.longDistance = false;
@ -93,11 +113,12 @@ public class Player extends LivingEntity {
particlePacket.offsetZ = 0.55f;
particlePacket.particleData = 0.25f;
particlePacket.particleCount = 100;
particlePacket.blockId = blockId;
playerConnection.sendPacket(particlePacket);
}
public void sendMessage(String message) {
ChatMessagePacket chatMessagePacket = new ChatMessagePacket("{\"text\": \"" + message + "\"}", ChatMessagePacket.Position.CHAT);
ChatMessagePacket chatMessagePacket = new ChatMessagePacket(Chat.rawText(message), ChatMessagePacket.Position.CHAT);
playerConnection.sendPacket(chatMessagePacket);
}
@ -162,6 +183,10 @@ public class Player extends LivingEntity {
return targetCustomBlock;
}
public Set<BossBar> getBossBars() {
return Collections.unmodifiableSet(bossBars);
}
public void openInventory(Inventory inventory) {
if (inventory == null)
throw new IllegalArgumentException("Inventory cannot be null, use Player#closeInventory() to close current");
@ -207,11 +232,11 @@ public class Player extends LivingEntity {
}
public void refreshSneaking(boolean sneaking) {
isSneaking = sneaking;
sneaking = sneaking;
}
public void refreshSprinting(boolean sprinting) {
isSprinting = sprinting;
sprinting = sprinting;
}
public void refreshKeepAlive(long lastKeepAlive) {
@ -238,6 +263,14 @@ public class Player extends LivingEntity {
this.targetBlockTime = 0;
}
public void refreshAddBossbar(BossBar bossBar) {
this.bossBars.add(bossBar);
}
public void refreshRemoveBossbar(BossBar bossBar) {
this.bossBars.remove(bossBar);
}
public long getLastKeepAlive() {
return lastKeepAlive;
}

View File

@ -5,7 +5,79 @@ import java.util.Arrays;
public enum Biome {
OCEAN(0),
DEEP_OCEAN(24),
FROZEN_OCEAN(10),
DEEP_FROZEN_OCEAN(50),
COLD_OCEAN(46),
DEEP_COLD_OCEAN(49),
LUKEWARM_OCEAN(45),
DEEP_LUKEWARM_OCEAN(48),
WARM_OCEAN(44),
DEEP_WARM_OCEAN(47),
RIVER(7),
FROZEN_RIVER(11),
BEACH(16),
STONE_SHORE(25),
SNOWY_BEACH(26),
FOREST(4),
WOODED_HILLS(18),
FLOWER_FOREST(132),
BIRCH_FOREST(27),
BIRCH_FOREST_HILLS(28),
TALL_BIRCH_FOREST(155),
TALL_BIRCH_HILLS(156),
DARK_FOREST(29),
DARK_FOREST_HILLS(157),
JUNGLE(21),
JUNGLE_HILLS(22),
MODIFIED_JUNGLE(149),
JUNGLE_EDGE(23),
MODIFIED_JUNGLE_EDGE(151),
BAMBOO_JUNGLE(168),
BAMBOO_JUNGLE_HILLS(169),
TAIGA(5),
TAIGA_HILLS(19),
TAIGA_MOUNTAINS(133),
SNOWY_TAIGA(30),
SNOWY_TAIGA_HILLS(31),
SNOWY_TAIGA_MOUNTAINS(158),
GIANT_TREE_TAIGA(32),
GIANT_TREE_TAIGA_HILLS(33),
GIANT_SPRUCE_TAIGA(160),
GIANT_SPRUCE_TAIGA_HILLS(161),
MUSHROOM_FIELDS(14),
MUSHROOM_FIELDS_SHORE(15),
SWAMP(6),
SWAMP_HILLS(134),
SAVANA(35),
SAVANA_PLATEAU(36),
SHATTERED_SAVANA(163),
SHATTERED_SAVANA_PLATEAU(164),
PLAINS(1),
SUNFLOWER_PLAINS(129),
DESERT(2),
DESERT_HILLS(17),
DESERT_LAKES(130),
SNOWY_TUNDRA(12),
SNOWY_MOUNTAINS(13),
ICE_SPIKES(140),
MOUNTAINS(3),
WOODED_MOUTAINS(34),
GRAVELLY_MOUNTAINS(131),
MODIFIED_GRAVELLY_MOUNTAINS(162),
MOUNTAIN_EDGE(20),
BADLANDS(37),
BADLANDS_PLATEAU(39),
MODIFIED_BADLANDS_PLATEAU(167),
WOODED_BADLANDS_PLATEAU(38),
MODIFIED_WOODED_BADLANDS_PLATEAU(166),
ERODED_BADLANDS(165),
NETHER(8),
THE_END(9),
SMALL_END_ISLANDS(40),
END_MIDLANDS(41),
END_HIGHLANDS(42),
END_BARRENS(43),
VOID(127);
private int id;

View File

@ -7,7 +7,7 @@ import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class BlockBatch {
public class BlockBatch implements BlockModifier {
private static volatile ExecutorService batchesPool = Executors.newFixedThreadPool(2);
@ -19,12 +19,9 @@ public class BlockBatch {
this.instance = instance;
}
@Override
public synchronized void setBlock(int x, int y, int z, short blockId) {
final int chunkX = Math.floorDiv(x, 16);
final int chunkZ = Math.floorDiv(z, 16);
Chunk chunk = this.instance.getChunk(chunkX, chunkZ);
if (chunk == null)
chunk = this.instance.createChunk(Biome.VOID, chunkX, chunkZ);
Chunk chunk = this.instance.getChunkAt(x, z);
List<BlockData> blockData = this.data.getOrDefault(chunk, new ArrayList<>());
BlockData data = new BlockData();
@ -38,6 +35,22 @@ public class BlockBatch {
this.data.put(chunk, blockData);
}
@Override
public void setBlock(int x, int y, int z, String blockId) {
Chunk chunk = this.instance.getChunkAt(x, z);
List<BlockData> blockData = this.data.getOrDefault(chunk, new ArrayList<>());
BlockData data = new BlockData();
data.x = x % 16;
data.y = y;
data.z = z % 16;
data.blockIdentifier = blockId;
blockData.add(data);
this.data.put(chunk, blockData);
}
public void flush() {
for (Map.Entry<Chunk, List<BlockData>> entry : data.entrySet()) {
Chunk chunk = entry.getKey();
@ -57,9 +70,14 @@ public class BlockBatch {
private int x, y, z;
private short blockId;
private String blockIdentifier;
public void apply(Chunk chunk) {
chunk.setBlock((byte) x, (byte) y, (byte) z, blockId);
if (blockIdentifier == null) {
chunk.setBlock((byte) x, (byte) y, (byte) z, blockId);
} else {
chunk.setBlock((byte) x, (byte) y, (byte) z, blockIdentifier);
}
}
}

View File

@ -6,13 +6,13 @@ import java.util.function.Supplier;
public class BlockManager {
private Map<Integer, CustomBlock> blocksInternalId = new HashMap<>();
private Map<Short, CustomBlock> blocksInternalId = new HashMap<>();
private Map<String, CustomBlock> blocksId = new HashMap<>();
public void registerBlock(Supplier<CustomBlock> blocks) {
CustomBlock customBlock = blocks.get();
String identifier = customBlock.getIdentifier();
int id = customBlock.getId();
short id = customBlock.getId();
this.blocksInternalId.put(id, customBlock);
this.blocksId.put(identifier, customBlock);
}
@ -21,7 +21,7 @@ public class BlockManager {
return blocksId.get(identifier);
}
public CustomBlock getBlock(int id) {
public CustomBlock getBlock(short id) {
return blocksInternalId.get(id);
}

View File

@ -0,0 +1,8 @@
package fr.themode.minestom.instance;
public interface BlockModifier {
void setBlock(int x, int y, int z, short blockId);
void setBlock(int x, int y, int z, String blockId);
}

View File

@ -3,6 +3,7 @@ package fr.themode.minestom.instance;
import fr.themode.minestom.Main;
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 java.util.Collections;
@ -13,12 +14,13 @@ public class Chunk {
private static final int CHUNK_SIZE = 16 * 256 * 16;
protected Set<ObjectEntity> objectEntities = new CopyOnWriteArraySet<>();
protected Set<EntityCreature> creatures = new CopyOnWriteArraySet<>();
protected Set<Player> players = new CopyOnWriteArraySet<>();
private int chunkX, chunkZ;
private Biome biome;
private short[] blocksId = new short[CHUNK_SIZE];
private int[] customBlocks = new int[CHUNK_SIZE];
private short[] customBlocks = new short[CHUNK_SIZE];
public Chunk(Biome biome, int chunkX, int chunkZ) {
this.biome = biome;
@ -29,9 +31,7 @@ public class Chunk {
protected void setBlock(byte x, byte y, byte z, short blockId) {
int index = getIndex(x, y, z);
this.blocksId[index] = blockId;
if (blockId == 0) {
this.customBlocks[index] = 0;
}
this.customBlocks[index] = 0;
}
protected void setBlock(byte x, byte y, byte z, String blockId) {
@ -49,7 +49,7 @@ public class Chunk {
}
public CustomBlock getCustomBlock(byte x, byte y, byte z) {
int id = this.customBlocks[getIndex(x, y, z)];
short id = this.customBlocks[getIndex(x, y, z)];
return id != 0 ? Main.getBlockManager().getBlock(id) : null;
}
@ -66,6 +66,12 @@ public class Chunk {
return;
this.creatures.add((EntityCreature) entity);
}
} else if (entity instanceof ObjectEntity) {
synchronized (objectEntities) {
if (this.objectEntities.contains(entity))
return;
this.objectEntities.add((ObjectEntity) entity);
}
}
}
@ -78,6 +84,10 @@ public class Chunk {
synchronized (creatures) {
this.creatures.remove(entity);
}
} else if (entity instanceof ObjectEntity) {
synchronized (objectEntities) {
this.objectEntities.remove(entity);
}
}
}
@ -97,6 +107,10 @@ public class Chunk {
return chunkZ;
}
public Set<ObjectEntity> getObjectEntities() {
return Collections.unmodifiableSet(objectEntities);
}
public Set<EntityCreature> getCreatures() {
return Collections.unmodifiableSet(creatures);
}

View File

@ -0,0 +1,71 @@
package fr.themode.minestom.instance;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ChunkBatch implements BlockModifier {
private static volatile ExecutorService batchesPool = Executors.newFixedThreadPool(3);
private Instance instance;
private Chunk chunk;
private List<BlockData> dataList = new ArrayList<>();
public ChunkBatch(Instance instance, Chunk chunk) {
this.instance = instance;
this.chunk = chunk;
}
@Override
public void setBlock(int x, int y, int z, short blockId) {
BlockData data = new BlockData();
data.x = (byte) x;
data.y = (byte) y;
data.z = (byte) z;
data.blockId = blockId;
this.dataList.add(data);
}
@Override
public void setBlock(int x, int y, int z, String blockId) {
BlockData data = new BlockData();
data.x = (byte) x;
data.y = (byte) y;
data.z = (byte) z;
data.blockIdentifier = blockId;
this.dataList.add(data);
}
public void flush() {
synchronized (chunk) {
batchesPool.submit(() -> {
for (BlockData data : dataList) {
data.apply(chunk);
}
instance.sendChunkUpdate(chunk); // TODO partial chunk data
});
}
}
private class BlockData {
private byte x, y, z;
private short blockId;
private String blockIdentifier;
public void apply(Chunk chunk) {
if (blockIdentifier == null) {
chunk.setBlock(x, y, z, blockId);
} else {
chunk.setBlock(x, y, z, blockIdentifier);
}
}
}
}

View File

@ -0,0 +1,9 @@
package fr.themode.minestom.instance;
public abstract class ChunkGenerator {
public abstract void generateChunkData(ChunkBatch batch, int chunkX, int chunkZ);
public abstract Biome getBiome(int chunkX, int chunkZ);
}

View File

@ -4,14 +4,18 @@ import fr.themode.minestom.entity.Player;
import java.util.concurrent.atomic.AtomicInteger;
/**
* TODO
* option to set the global as "global breaking" meaning that multiple players mining the same block will break it faster (cumulation)
*/
public abstract class CustomBlock {
private static final AtomicInteger idCounter = new AtomicInteger();
private int id;
private short id;
public CustomBlock() {
this.id = idCounter.incrementAndGet();
this.id = (short) idCounter.incrementAndGet();
}
public abstract short getType();
@ -23,7 +27,7 @@ public abstract class CustomBlock {
*/
public abstract int getBreakDelay(Player player);
public int getId() {
public short getId() {
return id;
}
}

View File

@ -1,30 +1,37 @@
package fr.themode.minestom.instance;
import fr.themode.minestom.Viewable;
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.ChunkDataPacket;
import fr.themode.minestom.net.packet.server.play.DestroyEntitiesPacket;
import fr.themode.minestom.utils.GroupedCollections;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
public class Instance {
public class Instance implements BlockModifier {
private UUID uniqueId;
private GroupedCollections<ObjectEntity> objectEntities = new GroupedCollections<>(new CopyOnWriteArrayList<>());
private GroupedCollections<EntityCreature> creatures = new GroupedCollections<>(new CopyOnWriteArrayList());
private GroupedCollections<Player> players = new GroupedCollections<>(new CopyOnWriteArrayList());
private Set<Chunk> chunksSet = new CopyOnWriteArraySet<>(); // TODO change for a map with position as key and chunk as value
private ChunkGenerator chunkGenerator;
private Map<Long, Chunk> chunks = new ConcurrentHashMap<>();
public Instance(UUID uniqueId) {
this.uniqueId = uniqueId;
}
@Override
public synchronized void setBlock(int x, int y, int z, short blockId) {
Chunk chunk = getChunkAt(x, z);
synchronized (chunk) {
@ -33,6 +40,7 @@ public class Instance {
}
}
@Override
public synchronized void setBlock(int x, int y, int z, String blockId) {
Chunk chunk = getChunkAt(x, z);
synchronized (chunk) {
@ -41,6 +49,11 @@ public class Instance {
}
}
public Chunk loadChunk(int chunkX, int chunkZ) {
Chunk chunk = getChunk(chunkX, chunkZ);
return chunk == null ? createChunk(chunkX, chunkZ) : chunk; // TODO load from file
}
public short getBlockId(int x, int y, int z) {
Chunk chunk = getChunkAt(x, z);
return chunk.getBlockId((byte) (x % 16), (byte) y, (byte) (z % 16));
@ -56,11 +69,7 @@ public class Instance {
}
public Chunk getChunk(int chunkX, int chunkZ) {
for (Chunk chunk : getChunks()) {
if (chunk.getChunkX() == chunkX && chunk.getChunkZ() == chunkZ)
return chunk;
}
return createChunk(Biome.VOID, chunkX, chunkZ); // TODO generation API
return chunks.getOrDefault(getChunkKey(chunkX, chunkZ), null);
}
public Chunk getChunkAt(double x, double z) {
@ -69,8 +78,12 @@ public class Instance {
return getChunk(chunkX, chunkZ);
}
public Set<Chunk> getChunks() {
return Collections.unmodifiableSet(chunksSet);
public void setChunkGenerator(ChunkGenerator chunkGenerator) {
this.chunkGenerator = chunkGenerator;
}
public Collection<Chunk> getChunks() {
return Collections.unmodifiableCollection(chunks.values());
}
public void addEntity(Entity entity) {
@ -79,9 +92,10 @@ public class Instance {
lastInstance.removeEntity(entity);
}
if (entity instanceof EntityCreature) {
if (entity instanceof Viewable) {
// TODO based on distance with players
getPlayers().forEach(p -> ((EntityCreature) entity).addViewer(p));
Viewable viewable = (Viewable) entity;
getPlayers().forEach(p -> ((Viewable) entity).addViewer(p));
} else if (entity instanceof Player) {
Player player = (Player) entity;
sendChunks(player);
@ -97,15 +111,23 @@ public class Instance {
if (entityInstance == null || entityInstance != this)
return;
if (entity instanceof EntityCreature) {
EntityCreature creature = (EntityCreature) entity;
creature.getViewers().forEach(p -> creature.removeViewer(p));
if (entity instanceof Viewable) {
DestroyEntitiesPacket destroyEntitiesPacket = new DestroyEntitiesPacket();
destroyEntitiesPacket.entityIds = new int[]{entity.getEntityId()};
Viewable viewable = (Viewable) entity;
viewable.getViewers().forEach(p -> p.getPlayerConnection().sendPacket(destroyEntitiesPacket)); // TODO destroy batch
viewable.getViewers().forEach(p -> viewable.removeViewer(p));
}
Chunk chunk = getChunkAt(entity.getX(), entity.getZ());
chunk.removeEntity(entity);
}
public GroupedCollections<ObjectEntity> getObjectEntities() {
return objectEntities;
}
public GroupedCollections<EntityCreature> getCreatures() {
return creatures;
}
@ -118,14 +140,25 @@ public class Instance {
return uniqueId;
}
protected Chunk createChunk(Biome biome, int chunkX, int chunkZ) {
protected Chunk createChunk(int chunkX, int chunkZ) {
Biome biome = chunkGenerator != null ? chunkGenerator.getBiome(chunkX, chunkZ) : Biome.VOID;
Chunk chunk = new Chunk(biome, chunkX, chunkZ);
this.objectEntities.addCollection(chunk.objectEntities);
this.creatures.addCollection(chunk.creatures);
this.players.addCollection(chunk.players);
this.chunksSet.add(chunk);
this.chunks.put(getChunkKey(chunkX, chunkZ), chunk);
if (chunkGenerator != null) {
ChunkBatch chunkBatch = createChunkBatch(chunk);
chunkGenerator.generateChunkData(chunkBatch, chunkX, chunkZ);
chunkBatch.flush();
}
return chunk;
}
protected ChunkBatch createChunkBatch(Chunk chunk) {
return new ChunkBatch(this, chunk);
}
protected void sendChunkUpdate(Chunk chunk) {
ChunkDataPacket chunkDataPacket = new ChunkDataPacket();
chunkDataPacket.fullChunk = false; // TODO partial chunk data
@ -141,4 +174,8 @@ public class Instance {
player.getPlayerConnection().sendPacket(chunkDataPacket);
}
}
private long getChunkKey(int chunkX, int chunkZ) {
return (((long) chunkX) << 32) | (chunkZ & 0xffffffffL);
}
}

View File

@ -0,0 +1,29 @@
package fr.themode.minestom.instance.demo;
import fr.themode.minestom.instance.Biome;
import fr.themode.minestom.instance.ChunkBatch;
import fr.themode.minestom.instance.ChunkGenerator;
import java.util.Random;
public class ChunkGeneratorDemo extends ChunkGenerator {
private final Random random = new Random();
@Override
public void generateChunkData(ChunkBatch batch, int chunkX, int chunkZ) {
for (byte x = 0; x < 16; x++)
for (byte z = 0; z < 16; z++) {
if (random.nextInt(2) == 1) {
batch.setBlock(x, (byte) 4, z, (short) 10);
} else {
batch.setBlock(x, (byte) 4, z, "custom_block");
}
}
}
@Override
public Biome getBiome(int chunkX, int chunkZ) {
return Biome.PLAINS;
}
}

View File

@ -7,12 +7,12 @@ public class StoneBlock extends CustomBlock {
@Override
public short getType() {
return 1;
return 117;
}
@Override
public String getIdentifier() {
return "stone_block";
return "custom_block";
}
@Override

View File

@ -1,5 +1,6 @@
package fr.themode.minestom.inventory;
import fr.themode.minestom.Viewable;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.item.ItemStack;
import fr.themode.minestom.net.packet.server.play.SetSlotPacket;
@ -12,7 +13,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicInteger;
public class Inventory implements InventoryModifier, InventoryClickHandler {
public class Inventory implements InventoryModifier, InventoryClickHandler, Viewable {
private static AtomicInteger lastInventoryId = new AtomicInteger();
@ -81,16 +82,19 @@ public class Inventory implements InventoryModifier, InventoryClickHandler {
getViewers().forEach(p -> p.getPlayerConnection().sendPacket(windowItemsPacket));
}
@Override
public Set<Player> getViewers() {
return Collections.unmodifiableSet(viewers);
}
@Override
public void addViewer(Player player) {
this.viewers.add(player);
WindowItemsPacket windowItemsPacket = getWindowItemsPacket();
player.getPlayerConnection().sendPacket(windowItemsPacket);
}
@Override
public void removeViewer(Player player) {
this.viewers.remove(player);
}

View File

@ -2,10 +2,15 @@ package fr.themode.minestom.net.packet.client.login;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.Main;
import fr.themode.minestom.bossbar.BarColor;
import fr.themode.minestom.bossbar.BarDivision;
import fr.themode.minestom.bossbar.BossBar;
import fr.themode.minestom.entity.GameMode;
import fr.themode.minestom.entity.ItemEntity;
import fr.themode.minestom.entity.Player;
import fr.themode.minestom.entity.demo.ChickenCreature;
import fr.themode.minestom.instance.Instance;
import fr.themode.minestom.instance.demo.ChunkGeneratorDemo;
import fr.themode.minestom.inventory.Inventory;
import fr.themode.minestom.inventory.InventoryType;
import fr.themode.minestom.inventory.PlayerInventory;
@ -34,11 +39,17 @@ public class LoginStartPacket implements ClientPreplayPacket {
private static Instance instance;
static {
ChunkGeneratorDemo chunkGeneratorDemo = new ChunkGeneratorDemo();
instance = Main.getInstanceManager().createInstance();
for (int x = -64; x < 64; x++)
for (int z = -64; z < 64; z++) {
instance.setBlock(x, 4, z, (short) 10);
instance.setChunkGenerator(chunkGeneratorDemo);
int loopStart = -4;
int loopEnd = 4;
long time = System.currentTimeMillis();
for (int x = loopStart; x < loopEnd; x++)
for (int z = loopStart; z < loopEnd; z++) {
instance.loadChunk(x, z);
}
System.out.println("Time to load all chunks: " + (System.currentTimeMillis() - time) + " ms");
}
@Override
@ -106,7 +117,6 @@ public class LoginStartPacket implements ClientPreplayPacket {
for (int x = 0; x < 4; x++)
for (int z = 0; z < 4; z++) {
// TODO test entity
ChickenCreature chickenCreature = new ChickenCreature();
chickenCreature.refreshPosition(0 + (double) x * 1, 5, 0 + (double) z * 1);
chickenCreature.setInstance(instance);
@ -151,6 +161,18 @@ public class LoginStartPacket implements ClientPreplayPacket {
player.openInventory(inv);
inv.setItemStack(1, new ItemStack(1, (byte) 2));
inv.updateItems();
BossBar bossBar = new BossBar("Le titre", BarColor.BLUE, BarDivision.SEGMENT_12);
bossBar.setProgress(0.75f);
bossBar.addViewer(player);
for (int x = 0; x < 4; x++)
for (int z = 0; z < 4; z++) {
ItemEntity itemEntity = new ItemEntity(new ItemStack(1, (byte) 32));
itemEntity.refreshPosition(x, 5, z);
itemEntity.setInstance(instance);
//itemEntity.remove();
}
}
@Override

View File

@ -25,7 +25,7 @@ public class ClientPlayerBlockPlacementPacket implements ClientPlayPacket {
int offsetY = blockFace == ClientPlayerDiggingPacket.BlockFace.BOTTOM ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.TOP ? 1 : 0;
int offsetZ = blockFace == ClientPlayerDiggingPacket.BlockFace.NORTH ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.SOUTH ? 1 : 0;
instance.setBlock(position.getX() + offsetX, position.getY() + offsetY, position.getZ() + offsetZ, "stone_block");
instance.setBlock(position.getX() + offsetX, position.getY() + offsetY, position.getZ() + offsetZ, "custom_block");
player.getInventory().refreshSlot(player.getHeldSlot());
// TODO consume block in hand for survival players
/*Random random = new Random();

View File

@ -0,0 +1,69 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.bossbar.BarColor;
import fr.themode.minestom.bossbar.BarDivision;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Utils;
import java.util.UUID;
public class BossBarPacket implements ServerPacket {
public UUID uuid;
public Action action;
public String title;
public float health;
public BarColor color;
public BarDivision division;
public byte flags;
@Override
public void write(Buffer buffer) {
Utils.writeUuid(buffer, uuid);
Utils.writeVarInt(buffer, action.ordinal());
switch (action) {
case ADD:
Utils.writeString(buffer, title);
buffer.putFloat(health);
Utils.writeVarInt(buffer, color.ordinal());
Utils.writeVarInt(buffer, division.ordinal());
buffer.putByte(flags);
break;
case REMOVE:
break;
case UPDATE_HEALTH:
buffer.putFloat(health);
break;
case UPDATE_TITLE:
Utils.writeString(buffer, title);
break;
case UPDATE_STYLE:
Utils.writeVarInt(buffer, color.ordinal());
Utils.writeVarInt(buffer, division.ordinal());
break;
case UPDATE_FLAGS:
buffer.putByte(flags);
break;
}
}
@Override
public int getId() {
return 0x0C;
}
public enum Action {
ADD,
REMOVE,
UPDATE_HEALTH,
UPDATE_TITLE,
UPDATE_STYLE,
UPDATE_FLAGS;
}
}

View File

@ -0,0 +1,22 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Utils;
public class DisplayScoreboardPacket implements ServerPacket {
public byte position;
public String scoreName;
@Override
public void write(Buffer buffer) {
buffer.putByte(position);
Utils.writeString(buffer, scoreName);
}
@Override
public int getId() {
return 0x42;
}
}

View File

@ -7,10 +7,13 @@ import fr.themode.minestom.utils.Utils;
public class EntityMetaDataPacket implements ServerPacket {
public int entityId;
public Buffer data;
@Override
public void write(Buffer buffer) {
Utils.writeVarInt(buffer, entityId);
buffer.putBuffer(data);
buffer.putByte((byte) 0xFF);
}
@Override

View File

@ -12,7 +12,8 @@ public class ParticlePacket implements ServerPacket {
public float offsetX, offsetY, offsetZ;
public float particleData;
public int particleCount;
// TODO data
public int blockId;
@Override
public void write(Buffer buffer) {
@ -26,7 +27,8 @@ public class ParticlePacket implements ServerPacket {
buffer.putFloat(offsetZ);
buffer.putFloat(particleData);
buffer.putInt(particleCount);
Utils.writeVarInt(buffer, 1);
if (particleId == 3)
Utils.writeVarInt(buffer, blockId);
}
@Override

View File

@ -0,0 +1,28 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Utils;
public class ScoreboardObjectivePacket implements ServerPacket {
public String objectiveName;
public byte mode;
public String objectiveValue;
public int type;
@Override
public void write(Buffer buffer) {
Utils.writeString(buffer, objectiveName);
buffer.putByte(mode);
if (mode == 0 || mode == 2) {
Utils.writeString(buffer, objectiveValue);
Utils.writeVarInt(buffer, type);
}
}
@Override
public int getId() {
return 0x49;
}
}

View File

@ -20,8 +20,7 @@ public class SpawnMobPacket implements ServerPacket {
@Override
public void write(Buffer buffer) {
Utils.writeVarInt(buffer, entityId);
buffer.putLong(entityUuid.getMostSignificantBits());
buffer.putLong(entityUuid.getLeastSignificantBits());
Utils.writeUuid(buffer, entityUuid);
Utils.writeVarInt(buffer, entityType);
buffer.putDouble(x);
buffer.putDouble(y);

View File

@ -0,0 +1,35 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Utils;
import java.util.UUID;
public class SpawnObjectPacket implements ServerPacket {
public int entityId;
public UUID uuid;
public int type;
public double x, y, z;
public float yaw, pitch;
public int data;
@Override
public void write(Buffer buffer) {
Utils.writeVarInt(buffer, entityId);
Utils.writeUuid(buffer, uuid);
Utils.writeVarInt(buffer, type);
buffer.putDouble(x);
buffer.putDouble(y);
buffer.putDouble(z);
buffer.putFloat(yaw);
buffer.putFloat(pitch);
buffer.putInt(data);
}
@Override
public int getId() {
return 0x00;
}
}

View File

@ -0,0 +1,72 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Utils;
public class TeamsPacket implements ServerPacket {
public String teamName;
public Action action;
public String teamDisplayName;
public byte friendlyFlags;
public String nameTagVisibility;
public String collisionRule;
public int teamColor;
public String teamPrefix;
public String teamSuffix;
public int entityCount;
public String[] entities;
@Override
public void write(Buffer buffer) {
Utils.writeString(buffer, teamName);
buffer.putByte((byte) action.ordinal());
switch (action) {
case CREATE_TEAM:
case UPDATE_TEAM_INFO:
Utils.writeString(buffer, teamDisplayName);
buffer.putByte(friendlyFlags);
Utils.writeString(buffer, nameTagVisibility);
Utils.writeString(buffer, collisionRule);
Utils.writeVarInt(buffer, teamColor);
Utils.writeString(buffer, teamPrefix);
Utils.writeString(buffer, teamSuffix);
break;
case REMOVE_TEAM:
break;
case ADD_PLAYERS_TEAM:
case REMOVE_PLAYERS_TEAM:
Utils.writeVarInt(buffer, entities.length);
for (String entity : entities) {
Utils.writeString(buffer, entity);
}
break;
}
if (action == Action.CREATE_TEAM) {
Utils.writeVarInt(buffer, entities.length);
for (String entity : entities) {
Utils.writeString(buffer, entity);
}
}
}
@Override
public int getId() {
return 0x4B;
}
public enum Action {
CREATE_TEAM,
REMOVE_TEAM,
UPDATE_TEAM_INFO,
ADD_PLAYERS_TEAM,
REMOVE_PLAYERS_TEAM;
}
}

View File

@ -0,0 +1,28 @@
package fr.themode.minestom.net.packet.server.play;
import fr.adamaq01.ozao.net.Buffer;
import fr.themode.minestom.net.packet.server.ServerPacket;
import fr.themode.minestom.utils.Utils;
public class UpdateScorePacket implements ServerPacket {
public String entityName;
public byte action;
public String objectiveName;
public int value;
@Override
public void write(Buffer buffer) {
Utils.writeString(buffer, entityName);
buffer.putByte(action);
Utils.writeString(buffer, objectiveName);
if (action != 1) {
Utils.writeVarInt(buffer, value);
}
}
@Override
public int getId() {
return 0x4C;
}
}

View File

@ -5,6 +5,7 @@ import fr.themode.minestom.item.ItemStack;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.UUID;
import java.util.stream.Collectors;
public class Utils {
@ -136,6 +137,11 @@ public class Utils {
return new Position(x, y, z);
}
public static void writeUuid(Buffer buffer, UUID uuid) {
buffer.putLong(uuid.getMostSignificantBits());
buffer.putLong(uuid.getLeastSignificantBits());
}
public static void writeItemStack(Buffer buffer, ItemStack itemStack) {
if (itemStack == null) {
buffer.putBoolean(false);