diff --git a/src/net/skidcode/gh/server/Server.java b/src/net/skidcode/gh/server/Server.java index 558ccc0..a900d98 100644 --- a/src/net/skidcode/gh/server/Server.java +++ b/src/net/skidcode/gh/server/Server.java @@ -41,7 +41,10 @@ public final class Server { public static final int PLUGIN_API_VERSION = 2; - public static boolean superSecretSettings = false; + public static boolean enableEntitySpawning = false; + + + public static boolean enableEntityTicking = false; public static boolean enableTNTEntity = false; public static volatile boolean running = true; public static RakNetHandler handler; @@ -68,7 +71,12 @@ public static void stop() { running = false; handler.notifyShutdown(); } - + + private static int freeEID = 1; + public static int incrementAndGetNextFreeEID() { + return ++freeEID; + } + public static void main(String[] args) throws IOException { Logger.info("Starting Server..."); Runtime.getRuntime().addShutdownHook(new Thread() { @@ -105,7 +113,7 @@ public void run() { {"allow-from-different-port", String.valueOf(Server.allowFromDifferentPort)}, {"enable-terminal-colors", String.valueOf(Server.enableColors)}, {"send-full-chunks", String.valueOf(Server.sendFullChunks)}, - {"enable-entity-ticking", String.valueOf(Server.superSecretSettings)}, + {"enable-entity-ticking", String.valueOf(Server.enableEntityTicking)}, {"enable-tnt-entity", String.valueOf(Server.enableTNTEntity)}, }); Server.enableColors = properties.getBoolean("enable-terminal-colors", Server.enableColors); @@ -117,10 +125,10 @@ public void run() { Server.allowFromDifferentPort = properties.getBoolean("allow-from-different-port", Server.allowFromDifferentPort); Server.sendFullChunks = properties.getBoolean("send-full-chunks", (Server.sendFullChunks)); Server.enableTNTEntity = properties.getBoolean("enable-tnt-entity", Server.enableTNTEntity); - Server.superSecretSettings = properties.getBoolean("enable-entity-ticking", Server.superSecretSettings); + Server.enableEntityTicking = properties.getBoolean("enable-entity-ticking", Server.enableEntityTicking); Logger.info("Running server on port "+Server.port); - if(Server.enableTNTEntity && !Server.superSecretSettings) { + if(Server.enableTNTEntity && !Server.enableEntityTicking) { Logger.warn("TNT Entity is enabled but entity ticking is not enabled!"); } diff --git a/src/net/skidcode/gh/server/entity/Entity.java b/src/net/skidcode/gh/server/entity/Entity.java index c1411e8..c493a0e 100644 --- a/src/net/skidcode/gh/server/entity/Entity.java +++ b/src/net/skidcode/gh/server/entity/Entity.java @@ -3,6 +3,8 @@ import java.util.ArrayList; import net.skidcode.gh.server.Server; +import net.skidcode.gh.server.network.protocol.RemoveEntityPacket; +import net.skidcode.gh.server.player.Player; import net.skidcode.gh.server.utils.AABB; import net.skidcode.gh.server.world.World; @@ -10,7 +12,7 @@ public abstract class Entity { public float posX = 0, posY = 0, posZ = 0; public float yaw, pitch; - public int eid; + public final int eid; public World world; public AABB boundingBox = new AABB(-0.5f, 0, -0.5f, 0.5f, 1, 0.5f); public float width = 1, height = 1, radius = 0.5f; @@ -21,7 +23,7 @@ public abstract class Entity { public Entity() { this.setPosition(Server.world.spawnX, Server.world.spawnY, Server.world.spawnZ, 0, 0); - this.eid = World.incrementAndGetNextFreeEID(); //TODO move to entity? + this.eid = Server.incrementAndGetNextFreeEID(); } public void setSize(float width, float height) { @@ -70,6 +72,13 @@ public void move(float mx, float my, float mz) { public void remove() { this.removed = true; + if(Server.enableEntitySpawning) { + for(Player p : this.world.players.values()) { + RemoveEntityPacket pk = new RemoveEntityPacket(); + pk.eid = this.eid; + p.dataPacket(pk); + } + } } public void tick() { @@ -82,4 +91,11 @@ public void setPosition(float x, float y, float z) { this.posZ = z; this.boundingBox.set(x - this.radius, y, z - this.radius, x + this.radius, y + this.height, z + this.radius); } + + public float distanceTo(float x, float y, float z) { + float dx = this.posX - x; + float dy = this.posY - y; + float dz = this.posZ - z; + return (float)Math.sqrt(dx*dx + dy*dy + dz*dz); + } } diff --git a/src/net/skidcode/gh/server/entity/PrimedTnt.java b/src/net/skidcode/gh/server/entity/PrimedTnt.java index 70469b3..74cccf0 100644 --- a/src/net/skidcode/gh/server/entity/PrimedTnt.java +++ b/src/net/skidcode/gh/server/entity/PrimedTnt.java @@ -1,5 +1,8 @@ package net.skidcode.gh.server.entity; +import net.skidcode.gh.server.Server; +import net.skidcode.gh.server.network.protocol.MovePlayerPacket; +import net.skidcode.gh.server.player.Player; import net.skidcode.gh.server.utils.Logger; import net.skidcode.gh.server.utils.MathUtils; import net.skidcode.gh.server.world.World; @@ -39,7 +42,20 @@ public void tick() { this.motionZ *= 0.7; this.motionY *= -0.5; } - System.out.println(this.posX+" "+this.posY+" "+this.posZ+" "+this.onGround); + + if(Server.enableEntitySpawning) { + for(Player p : this.world.players.values()) { + MovePlayerPacket pk = new MovePlayerPacket(); + pk.eid = this.eid; + pk.posX = this.posX; + pk.posY = this.posY; + pk.posZ = this.posZ; + pk.yaw = this.yaw; + pk.pitch = this.pitch; + p.dataPacket(pk); + } + } + --this.ticksUntilExplosion; if(this.ticksUntilExplosion > 0) { diff --git a/src/net/skidcode/gh/server/network/PacketWithEID.java b/src/net/skidcode/gh/server/network/PacketWithEID.java new file mode 100644 index 0000000..51e66ef --- /dev/null +++ b/src/net/skidcode/gh/server/network/PacketWithEID.java @@ -0,0 +1,6 @@ +package net.skidcode.gh.server.network; + +public interface PacketWithEID { + public int getEID(); + public void setEID(int eid); +} diff --git a/src/net/skidcode/gh/server/network/protocol/AddPlayerPacket.java b/src/net/skidcode/gh/server/network/protocol/AddPlayerPacket.java index 34ca7ec..4c51471 100644 --- a/src/net/skidcode/gh/server/network/protocol/AddPlayerPacket.java +++ b/src/net/skidcode/gh/server/network/protocol/AddPlayerPacket.java @@ -1,9 +1,10 @@ package net.skidcode.gh.server.network.protocol; import net.skidcode.gh.server.network.MinecraftDataPacket; +import net.skidcode.gh.server.network.PacketWithEID; import net.skidcode.gh.server.network.ProtocolInfo; -public class AddPlayerPacket extends MinecraftDataPacket{ +public class AddPlayerPacket extends MinecraftDataPacket implements PacketWithEID{ public long clientID; public String nickname; @@ -21,6 +22,16 @@ public void decode() { } + @Override + public int getEID() { + return this.eid; + } + + @Override + public void setEID(int eid) { + this.eid = eid; + } + @Override public void encode() { this.putByte(pid()); @@ -36,5 +47,4 @@ public void encode() { public int getSize() { return 1 + 8 + 2+this.nickname.length() + 4 + 4 + 4 + 4; } - } diff --git a/src/net/skidcode/gh/server/network/protocol/MovePlayerPacket.java b/src/net/skidcode/gh/server/network/protocol/MovePlayerPacket.java index 97c0899..4eb468e 100644 --- a/src/net/skidcode/gh/server/network/protocol/MovePlayerPacket.java +++ b/src/net/skidcode/gh/server/network/protocol/MovePlayerPacket.java @@ -1,9 +1,10 @@ package net.skidcode.gh.server.network.protocol; import net.skidcode.gh.server.network.MinecraftDataPacket; +import net.skidcode.gh.server.network.PacketWithEID; import net.skidcode.gh.server.network.ProtocolInfo; -public class MovePlayerPacket extends MinecraftDataPacket{ +public class MovePlayerPacket extends MinecraftDataPacket implements PacketWithEID{ public int eid; public float posX, posY, posZ, pitch, yaw; @@ -12,7 +13,17 @@ public class MovePlayerPacket extends MinecraftDataPacket{ public byte pid() { return ProtocolInfo.MOVE_PLAYER_PACKET; } + + @Override + public int getEID() { + return this.eid; + } + @Override + public void setEID(int eid) { + this.eid = eid; + } + @Override public void decode() { this.eid = this.getInt(); diff --git a/src/net/skidcode/gh/server/network/protocol/PlaceBlockPacket.java b/src/net/skidcode/gh/server/network/protocol/PlaceBlockPacket.java index c43e64e..39ed55d 100644 --- a/src/net/skidcode/gh/server/network/protocol/PlaceBlockPacket.java +++ b/src/net/skidcode/gh/server/network/protocol/PlaceBlockPacket.java @@ -1,9 +1,10 @@ package net.skidcode.gh.server.network.protocol; import net.skidcode.gh.server.network.MinecraftDataPacket; +import net.skidcode.gh.server.network.PacketWithEID; import net.skidcode.gh.server.network.ProtocolInfo; -public class PlaceBlockPacket extends MinecraftDataPacket{ +public class PlaceBlockPacket extends MinecraftDataPacket implements PacketWithEID{ public byte posY, face, id; public int eid, posX, posZ; @@ -12,6 +13,16 @@ public byte pid() { return ProtocolInfo.PLACE_BLOCK_PACKET; } + @Override + public int getEID() { + return this.eid; + } + + @Override + public void setEID(int eid) { + this.eid = eid; + } + @Override public void decode() { this.eid = this.getInt(); diff --git a/src/net/skidcode/gh/server/network/protocol/PlayerEquipmentPacket.java b/src/net/skidcode/gh/server/network/protocol/PlayerEquipmentPacket.java index 5a729ea..c096945 100644 --- a/src/net/skidcode/gh/server/network/protocol/PlayerEquipmentPacket.java +++ b/src/net/skidcode/gh/server/network/protocol/PlayerEquipmentPacket.java @@ -1,9 +1,10 @@ package net.skidcode.gh.server.network.protocol; import net.skidcode.gh.server.network.MinecraftDataPacket; +import net.skidcode.gh.server.network.PacketWithEID; import net.skidcode.gh.server.network.ProtocolInfo; -public class PlayerEquipmentPacket extends MinecraftDataPacket{ +public class PlayerEquipmentPacket extends MinecraftDataPacket implements PacketWithEID{ public int eid; public byte itemID; @@ -19,6 +20,16 @@ public void decode() { this.itemID = this.getByte(); } + @Override + public int getEID() { + return this.eid; + } + + @Override + public void setEID(int eid) { + this.eid = eid; + } + @Override public void encode() { this.putByte(pid()); diff --git a/src/net/skidcode/gh/server/network/protocol/RemoveBlockPacket.java b/src/net/skidcode/gh/server/network/protocol/RemoveBlockPacket.java index 90a2701..95b9c50 100644 --- a/src/net/skidcode/gh/server/network/protocol/RemoveBlockPacket.java +++ b/src/net/skidcode/gh/server/network/protocol/RemoveBlockPacket.java @@ -1,9 +1,10 @@ package net.skidcode.gh.server.network.protocol; import net.skidcode.gh.server.network.MinecraftDataPacket; +import net.skidcode.gh.server.network.PacketWithEID; import net.skidcode.gh.server.network.ProtocolInfo; -public class RemoveBlockPacket extends MinecraftDataPacket{ +public class RemoveBlockPacket extends MinecraftDataPacket implements PacketWithEID{ public int eid, posX, posZ; public byte posY; @@ -30,6 +31,17 @@ public void encode() { this.putByte(this.posY); } + @Override + public int getEID() { + return this.eid; + } + + @Override + public void setEID(int eid) { + this.eid = eid; + } + + @Override public int getSize() { return 1 + 4 + 4 + 4 + 1; diff --git a/src/net/skidcode/gh/server/network/protocol/RemoveEntityPacket.java b/src/net/skidcode/gh/server/network/protocol/RemoveEntityPacket.java index f1afb78..bd2fbbd 100644 --- a/src/net/skidcode/gh/server/network/protocol/RemoveEntityPacket.java +++ b/src/net/skidcode/gh/server/network/protocol/RemoveEntityPacket.java @@ -1,9 +1,10 @@ package net.skidcode.gh.server.network.protocol; import net.skidcode.gh.server.network.MinecraftDataPacket; +import net.skidcode.gh.server.network.PacketWithEID; import net.skidcode.gh.server.network.ProtocolInfo; -public class RemoveEntityPacket extends MinecraftDataPacket{ +public class RemoveEntityPacket extends MinecraftDataPacket implements PacketWithEID{ public int eid; @@ -17,6 +18,16 @@ public void decode() { } + @Override + public int getEID() { + return this.eid; + } + + @Override + public void setEID(int eid) { + this.eid = eid; + } + @Override public void encode() { this.putByte(this.pid()); diff --git a/src/net/skidcode/gh/server/network/protocol/StartGamePacket.java b/src/net/skidcode/gh/server/network/protocol/StartGamePacket.java index 10b0dbb..c1561ff 100644 --- a/src/net/skidcode/gh/server/network/protocol/StartGamePacket.java +++ b/src/net/skidcode/gh/server/network/protocol/StartGamePacket.java @@ -1,9 +1,10 @@ package net.skidcode.gh.server.network.protocol; import net.skidcode.gh.server.network.MinecraftDataPacket; +import net.skidcode.gh.server.network.PacketWithEID; import net.skidcode.gh.server.network.ProtocolInfo; -public class StartGamePacket extends MinecraftDataPacket{ +public class StartGamePacket extends MinecraftDataPacket implements PacketWithEID{ public float posX, posY, posZ; public int eid; @@ -17,7 +18,17 @@ public byte pid() { public void decode() { } + + @Override + public int getEID() { + return this.eid; + } + @Override + public void setEID(int eid) { + this.eid = eid; + } + @Override public void encode() { this.putByte(pid()); diff --git a/src/net/skidcode/gh/server/player/Player.java b/src/net/skidcode/gh/server/player/Player.java index 7457379..7b3fcc6 100644 --- a/src/net/skidcode/gh/server/player/Player.java +++ b/src/net/skidcode/gh/server/player/Player.java @@ -1,6 +1,7 @@ package net.skidcode.gh.server.player; import java.io.IOException; +import java.util.HashMap; import net.skidcode.gh.server.Server; import net.skidcode.gh.server.block.Block; @@ -8,9 +9,9 @@ import net.skidcode.gh.server.entity.Entity; import net.skidcode.gh.server.event.EventRegistry; import net.skidcode.gh.server.event.packet.DataPacketReceive; -import net.skidcode.gh.server.event.player.PlayerSendChatMessage; import net.skidcode.gh.server.item.ItemInstance; import net.skidcode.gh.server.network.MinecraftDataPacket; +import net.skidcode.gh.server.network.PacketWithEID; import net.skidcode.gh.server.network.ProtocolInfo; import net.skidcode.gh.server.network.protocol.AddPlayerPacket; import net.skidcode.gh.server.network.protocol.ChunkDataPacket; @@ -28,6 +29,14 @@ import net.skidcode.gh.server.world.format.PlayerData; public class Player extends Entity implements CommandIssuer{ + /** + * Used for spawning entities on client side. 0 should always be a player. + */ + private int localFreeEID = 0; + public int getNextLocalFreeEID() { + return this.localFreeEID++; + } + public long clientID; public int port; public int mtuSize; @@ -39,6 +48,25 @@ public class Player extends Entity implements CommandIssuer{ public GameMode gamemode; public boolean closed = false; + private HashMap eidServer2Local = new HashMap(); + private HashMap eidLocal2Server = new HashMap(); + + public int getLocalEID(int global) { + return this.eidServer2Local.getOrDefault(global, -1); + } + public int getGlobalEID(int local) { + return this.eidLocal2Server.getOrDefault(local, -1); + } + + public void registerEntity(Entity entity) { + int global = entity.eid; + if(this.eidServer2Local.containsKey(global)) return; + + int local = this.getNextLocalFreeEID(); + this.eidLocal2Server.put(local, global); + this.eidServer2Local.put(global, local); + } + public Player(String identifier, long clientID, String ip, int port) { super(); this.setSize(0.6f, 1.8f); @@ -55,7 +83,22 @@ public void sendMessage(String message) { this.dataPacket(pk); } + /** + * Sends a packet to client. + */ public void dataPacket(MinecraftDataPacket pk) { + if(pk instanceof PacketWithEID) { + PacketWithEID ei = (PacketWithEID) pk; + int prev = ei.getEID(); + ei.setEID(this.getLocalEID(ei.getEID())); + int current = ei.getEID(); + if(current < 0) { + Logger.warn(String.format("Packet(%d) has invalid local entity id(Global: %d, Local: %d, Player: %s)!", pk.pid(), prev, current, this.nickname)); + new Exception("Player::dataPacket stacktrace").printStackTrace(); + return; + } + } + Server.handler.sendPacket(this, pk); } @@ -75,6 +118,19 @@ public void onPlayerExit() { public void handlePacket(MinecraftDataPacket dp) { if(this.closed) return; EventRegistry.handleEvent(new DataPacketReceive(this, dp)); + + if(dp instanceof PacketWithEID) { + PacketWithEID ei = (PacketWithEID) dp; + int prev = ei.getEID(); + ei.setEID(this.getGlobalEID(ei.getEID())); + int current = ei.getEID(); + if(current < 0) { + Logger.warn(String.format("Packet(%d) has invalid global entity id(Global: %d, Local: %d, Player: %s)!", dp.pid(), current, prev, this.nickname)); + new Exception("Player::handlePacket stacktrace").printStackTrace(); + return; + } + } + packethandling: switch(dp.pid()) { case ProtocolInfo.MESSAGE_PACKET: @@ -102,8 +158,11 @@ public void handlePacket(MinecraftDataPacket dp) { e.printStackTrace(); Logger.error("Failed to parse playerdata!"); } - Server.world.addEntity(this); + + this.world = Server.world; + this.world.addPlayer(this); + this.registerEntity(this); StartGamePacket pk = new StartGamePacket(); pk.seed = this.world.worldSeed; @@ -115,16 +174,10 @@ public void handlePacket(MinecraftDataPacket dp) { for(Player player : this.world.players.values()) { if(player.eid != this.eid) { //TODO move to World::addPlayer ? - AddPlayerPacket pkk = new AddPlayerPacket(); - pkk.clientID = player.clientID; - pkk.eid = player.eid; - pkk.nickname = player.nickname; - pkk.posX = player.posX; - pkk.posY = player.posY; - pkk.posZ = player.posZ; - this.dataPacket(pkk); + this.spawnEntity(player); } } + Logger.info("Player "+this.nickname+" joined the game. Position: "+this.posX+", "+this.posY+", "+this.posZ); break; case ProtocolInfo.REMOVE_BLOCK_PACKET: @@ -241,6 +294,35 @@ public void handlePacket(MinecraftDataPacket dp) { } } + public void spawnEntity(Entity entity) { + if(entity instanceof Player) { + this.registerEntity(entity); + + Player player = (Player) entity; + + AddPlayerPacket pkk = new AddPlayerPacket(); + pkk.clientID = player.clientID; + pkk.eid = player.eid; + pkk.nickname = player.nickname; + pkk.posX = player.posX; + pkk.posY = player.posY; + pkk.posZ = player.posZ; + this.dataPacket(pkk); + }else { + if(Server.enableEntitySpawning) { + this.registerEntity(entity); + + AddPlayerPacket pkk = new AddPlayerPacket(); + pkk.clientID = 0xdedbeef; + pkk.eid = entity.eid; + pkk.nickname = entity.getClass().getName(); + pkk.posX = entity.posX; + pkk.posY = entity.posY; + pkk.posZ = entity.posZ; + this.dataPacket(pkk); + } + } + } public float getDestroySpeed() { return 0.5f; } @@ -257,14 +339,7 @@ public void onSpawned() { pep.itemID = p.itemID; this.dataPacket(pep); - AddPlayerPacket pkk2 = new AddPlayerPacket(); - pkk2.clientID = this.clientID; - pkk2.eid = this.eid; - pkk2.nickname = this.nickname; - pkk2.posX = this.posX; - pkk2.posY = this.posY; - pkk2.posZ = this.posZ; - p.dataPacket(pkk2); + p.spawnEntity(this); } } } diff --git a/src/net/skidcode/gh/server/world/Explosion.java b/src/net/skidcode/gh/server/world/Explosion.java index ebf1573..c36695e 100644 --- a/src/net/skidcode/gh/server/world/Explosion.java +++ b/src/net/skidcode/gh/server/world/Explosion.java @@ -1,9 +1,11 @@ package net.skidcode.gh.server.world; +import java.util.ArrayList; import java.util.HashSet; import net.skidcode.gh.server.block.Block; import net.skidcode.gh.server.entity.Entity; +import net.skidcode.gh.server.utils.AABB; import net.skidcode.gh.server.utils.BlockPos; import net.skidcode.gh.server.utils.MathUtils; import net.skidcode.gh.server.utils.random.BedrockRandom; @@ -68,6 +70,32 @@ public void explode() { } float saved = this.power; this.power *= 2.0f; + int minX = MathUtils.ffloor((this.xCenter - this.power) - 1); + int maxX = MathUtils.ffloor((this.xCenter + this.power) + 1); + int minY = MathUtils.ffloor((this.yCenter - this.power) - 1); + int maxY = MathUtils.ffloor((this.yCenter + this.power) + 1); + int minZ = MathUtils.ffloor((this.zCenter - this.power) - 1); + int maxZ = MathUtils.ffloor((this.zCenter + this.power) + 1); + + ArrayList entities = this.world.getEntities(this.source, new AABB(minX, minY, minZ, maxX, maxY, maxZ)); + for(Entity e : entities) { + float dist = e.distanceTo(this.xCenter, this.yCenter, this.zCenter) / this.power; + if(dist <= 1) { + float xd = e.posX - this.xCenter; + float yd = e.posY - this.yCenter; + float zd = e.posZ - this.zCenter; + float di = (float)Math.sqrt(xd*xd + yd*yd + zd*zd); + xd /= di; + yd /= di; + zd /= di; + float seen = (1 - di) * 1; //TODO getSeenPercent + //entity.hurt + e.motionX += xd*seen; + e.motionY += yd*seen; + e.motionZ += zd*seen; + } + } + //TODO damage entities here this.power = saved; diff --git a/src/net/skidcode/gh/server/world/World.java b/src/net/skidcode/gh/server/world/World.java index f8d1b28..b6511ba 100644 --- a/src/net/skidcode/gh/server/world/World.java +++ b/src/net/skidcode/gh/server/world/World.java @@ -30,7 +30,7 @@ public class World { public HashMap entities = new HashMap<>(); public ArrayList entitiesToAdd = new ArrayList<>(); public ArrayList entitiesToRemove = new ArrayList<>(); - private static int freeEID = 1; + public int worldSeed = 0x256512; public BedrockRandom random; public Chunk[][] chunks = new Chunk[16][16]; @@ -148,6 +148,7 @@ public boolean hasChunksAt(int minX, int minY, int minZ, int maxX, int maxY, int public void addPlayer(Player player) { this.players.put(player.eid, player); + this.addEntity(player); } public void removePlayer(int eid) { @@ -312,10 +313,6 @@ public void broadcastPacketFromPlayer(MinecraftDataPacket pk, Player p) { } } } - - public static int incrementAndGetNextFreeEID() { - return ++freeEID; - } public Material getMaterial(int x, int y, int z) { int id = this.getBlockIDAt(x, y, z); @@ -350,13 +347,19 @@ public void tick() { /*Timer*/ this.worldTime++; - if(Server.superSecretSettings) { + if(Server.enableEntityTicking) { for(int i = this.entitiesToAdd.size(); --i >= 0;) { Entity e = this.entitiesToAdd.remove(i); if(this.entities.containsKey(e.eid)) { Logger.warn("Entity with EID "+e.eid+" already exisists!"); }else { this.entities.put(e.eid, e); + + if(Server.enableEntitySpawning) { + for(Player p : this.players.values()) { + p.spawnEntity(e); + } + } } } @@ -660,5 +663,16 @@ public ArrayList getCubes(Entity entity, AABB aabb) { //TODO also check entities return list; } + + public ArrayList getEntities(Entity e, AABB aabb) { + ArrayList list = new ArrayList(); + for(Entity entity : this.entities.values()) { + if(e != entity && entity.boundingBox.intersects(aabb)){ + list.add(entity); + } + } + + return list; + } }