diff --git a/TrueCraft.API/Entities/IEntity.cs b/TrueCraft.API/Entities/IEntity.cs new file mode 100644 index 0000000..e604c3f --- /dev/null +++ b/TrueCraft.API/Entities/IEntity.cs @@ -0,0 +1,10 @@ +using System; + +namespace TrueCraft.API.Entities +{ + public interface IEntity + { + int EntityID { get; set; } + Vector3 Position { get; set; } + } +} \ No newline at end of file diff --git a/TrueCraft.API/Logging/LogCategory.cs b/TrueCraft.API/Logging/LogCategory.cs index 34474f2..00a1ad6 100644 --- a/TrueCraft.API/Logging/LogCategory.cs +++ b/TrueCraft.API/Logging/LogCategory.cs @@ -25,5 +25,6 @@ namespace TrueCraft.API.Logging /// Generally useful information. /// Notice = 16, + All = Packets | Debug | Warning | Error | Notice } } \ No newline at end of file diff --git a/TrueCraft.API/Networking/IMinecraftStream.cs b/TrueCraft.API/Networking/IMinecraftStream.cs index 4a4f72d..74a4006 100644 --- a/TrueCraft.API/Networking/IMinecraftStream.cs +++ b/TrueCraft.API/Networking/IMinecraftStream.cs @@ -5,6 +5,8 @@ namespace TrueCraft.API.Networking { public interface IMinecraftStream { + Stream BaseStream { get; } + byte ReadUInt8(); sbyte ReadInt8(); void WriteUInt8(byte value); diff --git a/TrueCraft.API/Networking/IRemoteClient.cs b/TrueCraft.API/Networking/IRemoteClient.cs index 82aa92f..c57743c 100644 --- a/TrueCraft.API/Networking/IRemoteClient.cs +++ b/TrueCraft.API/Networking/IRemoteClient.cs @@ -1,4 +1,6 @@ using System; +using TrueCraft.API.World; +using TrueCraft.API.Entities; namespace TrueCraft.API.Networking { @@ -6,6 +8,8 @@ namespace TrueCraft.API.Networking { IMinecraftStream MinecraftStream { get; } bool DataAvailable { get; } + IWorld World { get; } + IEntity Entity { get; } void QueuePacket(IPacket packet); } diff --git a/TrueCraft.API/Server/IEntityManager.cs b/TrueCraft.API/Server/IEntityManager.cs new file mode 100644 index 0000000..eaa9bb5 --- /dev/null +++ b/TrueCraft.API/Server/IEntityManager.cs @@ -0,0 +1,19 @@ +using System; +using TrueCraft.API.Entities; +using System.Collections.Generic; +using TrueCraft.API.Networking; + +namespace TrueCraft.API.Server +{ + public interface IEntityManager + { + /// + /// Adds an entity to the world and assigns it an entity ID. + /// + void SpawnEntity(IEntity entity); + void DespawnEntity(IEntity entity); + IEntity GetEntityByID(int id); + void Update(); + void SendEntitiesToClient(IRemoteClient client); + } +} \ No newline at end of file diff --git a/TrueCraft.API/Server/IMultiplayerServer.cs b/TrueCraft.API/Server/IMultiplayerServer.cs index 2cc0190..4edd038 100644 --- a/TrueCraft.API/Server/IMultiplayerServer.cs +++ b/TrueCraft.API/Server/IMultiplayerServer.cs @@ -25,5 +25,6 @@ namespace TrueCraft.API.Server void AddWorld(IWorld world); void AddLogProvider(ILogProvider provider); void Log(LogCategory category, string text, params object[] parameters); + IEntityManager GetEntityManagerForWorld(IWorld world); } } \ No newline at end of file diff --git a/TrueCraft.API/TrueCraft.API.csproj b/TrueCraft.API/TrueCraft.API.csproj index 8ad081f..870f3d0 100644 --- a/TrueCraft.API/TrueCraft.API.csproj +++ b/TrueCraft.API/TrueCraft.API.csproj @@ -56,13 +56,14 @@ - + + @@ -70,6 +71,7 @@ + diff --git a/TrueCraft.API/World/IChunk.cs b/TrueCraft.API/World/IChunk.cs index 89f9b1e..784bf80 100644 --- a/TrueCraft.API/World/IChunk.cs +++ b/TrueCraft.API/World/IChunk.cs @@ -7,9 +7,12 @@ namespace TrueCraft.API.World Coordinates2D Coordinates { get; set; } bool IsModified { get; set; } int[] HeightMap { get; } - ISection[] Sections { get; } byte[] Biomes { get; } DateTime LastAccessed { get; set; } + byte[] Blocks { get; } + NibbleArray Metadata { get; } + NibbleArray BlockLight { get; } + NibbleArray SkyLight { get; } byte GetBlockID(Coordinates3D coordinates); byte GetMetadata(Coordinates3D coordinates); byte GetSkyLight(Coordinates3D coordinates); diff --git a/TrueCraft.API/World/ISection.cs b/TrueCraft.API/World/ISection.cs deleted file mode 100644 index 883fb7c..0000000 --- a/TrueCraft.API/World/ISection.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; - -namespace TrueCraft.API.World -{ - public interface ISection - { - byte[] Blocks { get; } - NibbleArray Metadata { get; } - NibbleArray BlockLight { get; } - NibbleArray SkyLight { get; } - byte Y { get; } - byte GetBlockID(Coordinates3D coordinates); - byte GetMetadata(Coordinates3D coordinates); - byte GetSkyLight(Coordinates3D coordinates); - byte GetBlockLight(Coordinates3D coordinates); - void SetBlockID(Coordinates3D coordinates, byte value); - void SetMetadata(Coordinates3D coordinates, byte value); - void SetSkyLight(Coordinates3D coordinates, byte value); - void SetBlockLight(Coordinates3D coordinates, byte value); - void ProcessSection(); - } -} \ No newline at end of file diff --git a/TrueCraft.Core/Networking/BufferedStream.cs b/TrueCraft.Core/Networking/BufferedStream.cs new file mode 100644 index 0000000..32f7440 --- /dev/null +++ b/TrueCraft.Core/Networking/BufferedStream.cs @@ -0,0 +1,77 @@ +using System; +using System.IO; + +namespace TrueCraft.Core.Networking +{ + /// + /// Queues all writes until Stream.Flush() is called. This is different than System.IO.BufferedStream. + /// + public class BufferedStream : Stream + { + public BufferedStream(Stream baseStream) + { + BaseStream = baseStream; + PendingStream = new MemoryStream(512); + WriteImmediately = false; + } + + public Stream BaseStream { get; set; } + public MemoryStream PendingStream { get; set; } + + /// + /// Used by PacketReader to insert the ID and length into the stream before the packet contents. + /// + internal bool WriteImmediately { get; set; } + + public override bool CanRead { get { return BaseStream.CanRead; } } + + public override bool CanSeek { get { return BaseStream.CanSeek; } } + + public override bool CanWrite { get { return BaseStream.CanWrite; } } + + public override void Flush() + { + BaseStream.Write(PendingStream.GetBuffer(), 0, (int)PendingStream.Position); + PendingStream.Position = 0; + } + + public long PendingWrites + { + get { return PendingStream.Position; } + } + + public override long Length + { + get { return BaseStream.Length; } + } + + public override long Position + { + get { return BaseStream.Position; } + set { BaseStream.Position = value; } + } + + public override int Read(byte[] buffer, int offset, int count) + { + return BaseStream.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) + { + return BaseStream.Seek(offset, origin); + } + + public override void SetLength(long value) + { + BaseStream.SetLength(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + if (WriteImmediately) + BaseStream.Write(buffer, offset, count); + else + PendingStream.Write(buffer, offset, count); + } + } +} diff --git a/TrueCraft.Core/Networking/PacketReader.cs b/TrueCraft.Core/Networking/PacketReader.cs index 15d683e..0bdb89f 100644 --- a/TrueCraft.Core/Networking/PacketReader.cs +++ b/TrueCraft.Core/Networking/PacketReader.cs @@ -32,13 +32,13 @@ namespace TrueCraft.Core.Networking RegisterPacketType(serverbound: true, clientbound: false); // 0x0A RegisterPacketType(serverbound: true, clientbound: false); // 0x0B RegisterPacketType(serverbound: true, clientbound: false); // 0x0C - RegisterPacketType(serverbound: true, clientbound: false); // 0x0D + RegisterPacketType(serverbound: true, clientbound: false); // 0x0D RegisterPacketType(serverbound: false, clientbound: true); // 0x0D RegisterPacketType(serverbound: true, clientbound: false); // 0x0E RegisterPacketType(serverbound: true, clientbound: false); // 0x0F RegisterPacketType(serverbound: true, clientbound: false); // 0x10 RegisterPacketType(serverbound: false, clientbound: true); // 0x11 - RegisterPacketType(serverbound: false, clientbound: true); // 0x12 + RegisterPacketType(); // 0x12 RegisterPacketType(serverbound: true, clientbound: false); // 0x13 RegisterPacketType(serverbound: false, clientbound: true); // 0x14 RegisterPacketType(serverbound: false, clientbound: true); // 0x15 @@ -114,6 +114,7 @@ namespace TrueCraft.Core.Networking { stream.WriteUInt8(packet.ID); packet.WritePacket(stream); + stream.BaseStream.Flush(); } } } \ No newline at end of file diff --git a/TrueCraft.Core/Networking/Packets/ChatMessagePacket.cs b/TrueCraft.Core/Networking/Packets/ChatMessagePacket.cs index d304d1c..05b558d 100644 --- a/TrueCraft.Core/Networking/Packets/ChatMessagePacket.cs +++ b/TrueCraft.Core/Networking/Packets/ChatMessagePacket.cs @@ -12,6 +12,11 @@ namespace TrueCraft.Core.Networking.Packets { public byte ID { get { return 0x03; } } + public ChatMessagePacket(string message) + { + Message = message; + } + public string Message; public void ReadPacket(IMinecraftStream stream) diff --git a/TrueCraft.Core/Networking/Packets/ChunkDataPacket.cs b/TrueCraft.Core/Networking/Packets/ChunkDataPacket.cs index b68e46b..3efdee8 100644 --- a/TrueCraft.Core/Networking/Packets/ChunkDataPacket.cs +++ b/TrueCraft.Core/Networking/Packets/ChunkDataPacket.cs @@ -10,6 +10,17 @@ namespace TrueCraft.Core.Networking.Packets { public byte ID { get { return 0x33; } } + public ChunkDataPacket(int x, short y, int z, short width, short height, short depth, byte[] compressedData) + { + X = x; + Y = y; + Z = z; + Width = width; + Height = height; + Depth = depth; + CompressedData = compressedData; + } + public int X; public short Y; public int Z; diff --git a/TrueCraft.Core/Networking/Packets/ChunkPreamblePacket.cs b/TrueCraft.Core/Networking/Packets/ChunkPreamblePacket.cs index 883422d..f41ecea 100644 --- a/TrueCraft.Core/Networking/Packets/ChunkPreamblePacket.cs +++ b/TrueCraft.Core/Networking/Packets/ChunkPreamblePacket.cs @@ -10,6 +10,13 @@ namespace TrueCraft.Core.Networking.Packets { public byte ID { get { return 0x32; } } + public ChunkPreamblePacket(int x, int z, bool load = true) + { + X = x; + Z = z; + Load = load; + } + public int X, Z; /// /// If false, free the chunk. If true, allocate it. diff --git a/TrueCraft.Core/Networking/Packets/PlayerPositionAndLookPacket.cs b/TrueCraft.Core/Networking/Packets/PlayerPositionAndLookPacket.cs index 830d4f4..6d75cf3 100644 --- a/TrueCraft.Core/Networking/Packets/PlayerPositionAndLookPacket.cs +++ b/TrueCraft.Core/Networking/Packets/PlayerPositionAndLookPacket.cs @@ -11,6 +11,17 @@ namespace TrueCraft.Core.Networking.Packets { public byte ID { get { return 0x0D; } } + public PlayerPositionAndLookPacket(double x, double y, double stance, double z, float yaw, float pitch, bool onGround) + { + X = x; + Y = y; + Z = z; + Stance = stance; + Yaw = yaw; + Pitch = pitch; + OnGround = onGround; + } + public double X, Y, Z; public double Stance; public float Yaw, Pitch; @@ -33,8 +44,8 @@ namespace TrueCraft.Core.Networking.Packets stream.WriteDouble(Y); stream.WriteDouble(Stance); stream.WriteDouble(Z); - stream.WriteDouble(Yaw); - stream.WriteDouble(Pitch); + stream.WriteSingle(Yaw); + stream.WriteSingle(Pitch); stream.WriteBoolean(OnGround); } } diff --git a/TrueCraft.Core/Networking/Packets/SetPlayerPositionPacket.cs b/TrueCraft.Core/Networking/Packets/SetPlayerPositionPacket.cs index 095413c..5b3415e 100644 --- a/TrueCraft.Core/Networking/Packets/SetPlayerPositionPacket.cs +++ b/TrueCraft.Core/Networking/Packets/SetPlayerPositionPacket.cs @@ -10,6 +10,17 @@ namespace TrueCraft.Core.Networking.Packets { public byte ID { get { return 0x0D; } } + public SetPlayerPositionPacket(double x, double y, double stance, double z, float yaw, float pitch, bool onGround) + { + X = x; + Y = y; + Z = z; + Stance = stance; + Yaw = yaw; + Pitch = pitch; + OnGround = onGround; + } + public double X, Y, Z; public double Stance; public float Yaw, Pitch; @@ -32,8 +43,8 @@ namespace TrueCraft.Core.Networking.Packets stream.WriteDouble(Stance); stream.WriteDouble(Y); stream.WriteDouble(Z); - stream.WriteDouble(Yaw); - stream.WriteDouble(Pitch); + stream.WriteSingle(Yaw); + stream.WriteSingle(Pitch); stream.WriteBoolean(OnGround); } } diff --git a/TrueCraft.Core/TrueCraft.Core.csproj b/TrueCraft.Core/TrueCraft.Core.csproj index d31aa0d..ca10e3d 100644 --- a/TrueCraft.Core/TrueCraft.Core.csproj +++ b/TrueCraft.Core/TrueCraft.Core.csproj @@ -102,6 +102,7 @@ + diff --git a/TrueCraft.Core/World/Chunk.cs b/TrueCraft.Core/World/Chunk.cs index 6f9f293..2849259 100644 --- a/TrueCraft.Core/World/Chunk.cs +++ b/TrueCraft.Core/World/Chunk.cs @@ -12,25 +12,26 @@ namespace TrueCraft.Core.World { public class Chunk : INbtSerializable, IChunk { - public const int Width = 16, Height = 256, Depth = 16; + public const int Width = 16, Height = 128, Depth = 16; private static readonly NbtSerializer Serializer = new NbtSerializer(typeof(Chunk)); [NbtIgnore] public DateTime LastAccessed { get; set; } - - public bool IsModified { get; set; } - - public byte[] Biomes { get; set; } - - public int[] HeightMap { get; set; } - [NbtIgnore] - public ISection[] Sections { get; set; } - + public bool IsModified { get; set; } + [NbtIgnore] + public byte[] Blocks { get; set; } + [NbtIgnore] + public NibbleArray Metadata { get; set; } + [NbtIgnore] + public NibbleArray BlockLight { get; set; } + [NbtIgnore] + public NibbleArray SkyLight { get; set; } + public byte[] Biomes { get; set; } + public int[] HeightMap { get; set; } [TagName("xPos")] public int X { get; set; } - [TagName("zPos")] public int Z { get; set; } @@ -57,9 +58,6 @@ namespace TrueCraft.Core.World public Chunk() { TerrainPopulated = true; - Sections = new Section[16]; - for (int i = 0; i < Sections.Length; i++) - Sections[i] = new Section((byte)i); Biomes = new byte[Width * Depth]; HeightMap = new int[Width * Depth]; LastAccessed = DateTime.Now; @@ -69,47 +67,49 @@ namespace TrueCraft.Core.World { X = coordinates.X; Z = coordinates.Z; + const int size = Width * Height * Depth; + Blocks = new byte[size]; + Metadata = new NibbleArray(size); + BlockLight = new NibbleArray(size); + SkyLight = new NibbleArray(size); + for (int i = 0; i < size; i++) + SkyLight[i] = 0xFF; } public byte GetBlockID(Coordinates3D coordinates) { LastAccessed = DateTime.Now; - int section = GetSectionNumber(coordinates.Y); - coordinates.Y = GetPositionInSection(coordinates.Y); - return Sections[section].GetBlockID(coordinates); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width); + return Blocks[index]; } public byte GetMetadata(Coordinates3D coordinates) { LastAccessed = DateTime.Now; - int section = GetSectionNumber(coordinates.Y); - coordinates.Y = GetPositionInSection(coordinates.Y); - return Sections[section].GetMetadata(coordinates); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width); + return Metadata[index]; } public byte GetSkyLight(Coordinates3D coordinates) { LastAccessed = DateTime.Now; - int section = GetSectionNumber(coordinates.Y); - coordinates.Y = GetPositionInSection(coordinates.Y); - return Sections[section].GetSkyLight(coordinates); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width); + return SkyLight[index]; } public byte GetBlockLight(Coordinates3D coordinates) { LastAccessed = DateTime.Now; - int section = GetSectionNumber(coordinates.Y); - coordinates.Y = GetPositionInSection(coordinates.Y); - return Sections[section].GetBlockLight(coordinates); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width); + return BlockLight[index]; } public void SetBlockID(Coordinates3D coordinates, byte value) { LastAccessed = DateTime.Now; IsModified = true; - int section = GetSectionNumber(coordinates.Y); - coordinates.Y = GetPositionInSection(coordinates.Y); - Sections[section].SetBlockID(coordinates, value); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width); + Blocks[index] = value; var oldHeight = GetHeight((byte)coordinates.X, (byte)coordinates.Z); if (value == 0) // Air { @@ -135,37 +135,24 @@ namespace TrueCraft.Core.World { LastAccessed = DateTime.Now; IsModified = true; - int section = GetSectionNumber(coordinates.Y); - coordinates.Y = GetPositionInSection(coordinates.Y); - Sections[section].SetMetadata(coordinates, value); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width); + Metadata[index] = value; } public void SetSkyLight(Coordinates3D coordinates, byte value) { LastAccessed = DateTime.Now; IsModified = true; - int section = GetSectionNumber(coordinates.Y); - coordinates.Y = GetPositionInSection(coordinates.Y); - Sections[section].SetSkyLight(coordinates, value); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width); + SkyLight[index] = value; } public void SetBlockLight(Coordinates3D coordinates, byte value) { LastAccessed = DateTime.Now; IsModified = true; - int section = GetSectionNumber(coordinates.Y); - coordinates.Y = GetPositionInSection(coordinates.Y); - Sections[section].SetBlockLight(coordinates, value); - } - - private static int GetSectionNumber(int yPos) - { - return yPos / 16; - } - - private static int GetPositionInSection(int yPos) - { - return yPos % 16; + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width); + BlockLight[index] = value; } /// @@ -206,19 +193,7 @@ namespace TrueCraft.Core.World var chunk = (NbtCompound)Serializer.Serialize(this, tagName, true); var entities = new NbtList("Entities", NbtTagType.Compound); chunk.Add(entities); - var sections = new NbtList("Sections", NbtTagType.Compound); - var serializer = new NbtSerializer(typeof(Section)); - for (int i = 0; i < Sections.Length; i++) - { - if (Sections[i] is Section) - { - if (!(Sections[i] as Section).IsAir) - sections.Add(serializer.Serialize(Sections[i])); - } - else - sections.Add(serializer.Serialize(Sections[i])); - } - chunk.Add(sections); + // TODO: Save block data return chunk; } @@ -231,18 +206,11 @@ namespace TrueCraft.Core.World this.Biomes = chunk.Biomes; this.HeightMap = chunk.HeightMap; this.LastUpdate = chunk.LastUpdate; - this.Sections = chunk.Sections; this.TerrainPopulated = chunk.TerrainPopulated; this.X = chunk.X; this.Z = chunk.Z; - var serializer = new NbtSerializer(typeof(Section)); - foreach (var section in compound["Sections"] as NbtList) - { - int index = section["Y"].IntValue; - Sections[index] = (Section)serializer.Deserialize(section); - Sections[index].ProcessSection(); - } + // TODO: Load block data } } } diff --git a/TrueCraft.Core/World/Section.cs b/TrueCraft.Core/World/Section.cs index ac2e078..9ec220f 100644 --- a/TrueCraft.Core/World/Section.cs +++ b/TrueCraft.Core/World/Section.cs @@ -4,7 +4,7 @@ using TrueCraft.API.World; namespace TrueCraft.Core.World { - public class Section : ISection + public class Section { public const byte Width = 16, Height = 16, Depth = 16; @@ -42,31 +42,31 @@ namespace TrueCraft.Core.World public byte GetBlockID(Coordinates3D coordinates) { - int index = coordinates.X + (coordinates.Z * Width) + (coordinates.Y * Height * Width); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Depth * Width); return Blocks[index]; } public byte GetMetadata(Coordinates3D coordinates) { - int index = coordinates.X + (coordinates.Z * Width) + (coordinates.Y * Height * Width); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Depth * Width); return Metadata[index]; } public byte GetSkyLight(Coordinates3D coordinates) { - int index = coordinates.X + (coordinates.Z * Width) + (coordinates.Y * Height * Width); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Depth * Width); return SkyLight[index]; } public byte GetBlockLight(Coordinates3D coordinates) { - int index = coordinates.X + (coordinates.Z * Width) + (coordinates.Y * Height * Width); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Depth * Width); return BlockLight[index]; } public void SetBlockID(Coordinates3D coordinates, byte value) { - int index = coordinates.X + (coordinates.Z * Width) + (coordinates.Y * Height * Width); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Depth * Width); if (value == 0) { if (Blocks[index] != 0) @@ -82,19 +82,19 @@ namespace TrueCraft.Core.World public void SetMetadata(Coordinates3D coordinates, byte value) { - int index = coordinates.X + (coordinates.Z * Width) + (coordinates.Y * Height * Width); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Depth * Width); Metadata[index] = value; } public void SetSkyLight(Coordinates3D coordinates, byte value) { - int index = coordinates.X + (coordinates.Z * Width) + (coordinates.Y * Height * Width); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Depth * Width); SkyLight[index] = value; } public void SetBlockLight(Coordinates3D coordinates, byte value) { - int index = coordinates.X + (coordinates.Z * Width) + (coordinates.Y * Height * Width); + int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Depth * Width); BlockLight[index] = value; } diff --git a/TrueCraft/Entities/PlayerEntity.cs b/TrueCraft/Entities/PlayerEntity.cs new file mode 100644 index 0000000..1caae22 --- /dev/null +++ b/TrueCraft/Entities/PlayerEntity.cs @@ -0,0 +1,16 @@ +using System; +using TrueCraft.API.Entities; +using TrueCraft.API; + +namespace TrueCraft.Entities +{ + public class PlayerEntity : IEntity + { + public PlayerEntity() + { + } + + public int EntityID { get; set; } + public Vector3 Position { get; set; } + } +} \ No newline at end of file diff --git a/TrueCraft/EntityManager.cs b/TrueCraft/EntityManager.cs new file mode 100644 index 0000000..7ca567c --- /dev/null +++ b/TrueCraft/EntityManager.cs @@ -0,0 +1,48 @@ +using System; +using TrueCraft.API.Server; +using TrueCraft.API.World; +using TrueCraft.API.Entities; +using TrueCraft.API.Networking; + +namespace TrueCraft +{ + public class EntityManager : IEntityManager + { + public IWorld World { get; set; } + public IMultiplayerServer Server { get; set; } + + private int NextEntityID { get; set; } + + public EntityManager(IMultiplayerServer server, IWorld world) + { + Server = server; + World = world; + NextEntityID = 1; // TODO: Handle loading worlds that already have entities + } + + public void SpawnEntity(IEntity entity) + { + entity.EntityID = NextEntityID++; + } + + public void DespawnEntity(IEntity entity) + { + throw new NotImplementedException(); + } + + public IEntity GetEntityByID(int id) + { + throw new NotImplementedException(); + } + + public void Update() + { + throw new NotImplementedException(); + } + + public void SendEntitiesToClient(IRemoteClient client) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/TrueCraft/Handlers/LoginHandlers.cs b/TrueCraft/Handlers/LoginHandlers.cs index d7c1186..b681efc 100644 --- a/TrueCraft/Handlers/LoginHandlers.cs +++ b/TrueCraft/Handlers/LoginHandlers.cs @@ -3,6 +3,8 @@ using TrueCraft.API.Server; using TrueCraft.API.Networking; using TrueCraft.Core.Networking.Packets; using TrueCraft.API.Logging; +using TrueCraft.API; +using TrueCraft.Entities; namespace TrueCraft.Handlers { @@ -21,16 +23,24 @@ namespace TrueCraft.Handlers var packet = (LoginRequestPacket)_packet; var client = (RemoteClient)_client; if (packet.ProtocolVersion < server.PacketReader.ProtocolVersion) - client.QueuePacket(new DisconnectPacket("Client outdated! Use beta 1.7.3!")); + client.QueuePacket(new DisconnectPacket("Client outdated! Use beta 1.7.3.")); else if (packet.ProtocolVersion > server.PacketReader.ProtocolVersion) - client.QueuePacket(new DisconnectPacket("Server outdated! Use beta 1.7.3!")); + client.QueuePacket(new DisconnectPacket("Server outdated! Use beta 1.7.3.")); else if (server.Worlds.Count == 0) client.QueuePacket(new DisconnectPacket("Server has no worlds configured.")); else { - server.Log(LogCategory.Notice, "{0} joined the server.", client.Username); + server.Log(LogCategory.Notice, "{0} joined the server.", client.Username); // TODO: Mention the same thing in chat client.LoggedIn = true; - server.Scheduler.ScheduleEvent(DateTime.Now.AddSeconds(3), (s) => client.QueuePacket(new DisconnectPacket("Bye!"))); + client.Entity = new PlayerEntity(); + client.World = server.Worlds[0]; + server.GetEntityManagerForWorld(client.World).SpawnEntity(client.Entity); + client.QueuePacket(new LoginResponsePacket(0, 0, Dimension.Overworld)); + client.ChunkRadius = 4; + client.UpdateChunks(); + client.QueuePacket(new SpawnPositionPacket(0, 16, 0)); + client.QueuePacket(new SetPlayerPositionPacket(0, 16, 17, 0, 0, 0, true)); + client.QueuePacket(new ChatMessagePacket(string.Format("Welcome to the server, {0}!", client.Username))); } } } diff --git a/TrueCraft/MultiplayerServer.cs b/TrueCraft/MultiplayerServer.cs index 48b42f1..7bd1cb4 100644 --- a/TrueCraft/MultiplayerServer.cs +++ b/TrueCraft/MultiplayerServer.cs @@ -17,6 +17,7 @@ namespace TrueCraft public IPacketReader PacketReader { get; private set; } public IList Clients { get; private set; } public IList Worlds { get; private set; } + public IList EntityManagers { get; private set; } public IEventScheduler Scheduler { get; private set; } private Timer NetworkWorker, EnvironmentWorker; @@ -33,6 +34,7 @@ namespace TrueCraft EnvironmentWorker = new Timer(DoEnvironment); PacketHandlers = new PacketHandler[0x100]; Worlds = new List(); + EntityManagers = new List(); LogProviders = new List(); Scheduler = new EventScheduler(this); @@ -58,6 +60,8 @@ namespace TrueCraft public void AddWorld(IWorld world) { Worlds.Add(world); + var manager = new EntityManager(this, world); + EntityManagers.Add(manager); } public void AddLogProvider(ILogProvider provider) @@ -74,6 +78,17 @@ namespace TrueCraft } } + public IEntityManager GetEntityManagerForWorld(IWorld world) + { + for (int i = 0; i < EntityManagers.Count; i++) + { + var manager = EntityManagers[i] as EntityManager; + if (manager.World == world) + return manager; + } + return null; + } + private void DisconnectClient(IRemoteClient _client) { var client = (RemoteClient)_client; @@ -85,7 +100,7 @@ namespace TrueCraft private void AcceptClient(IAsyncResult result) { var tcpClient = Listener.EndAcceptTcpClient(result); - var client = new RemoteClient(tcpClient.GetStream()); + var client = new RemoteClient(this, tcpClient.GetStream()); Clients.Add(client); Listener.BeginAcceptTcpClient(AcceptClient, null); } @@ -105,6 +120,7 @@ namespace TrueCraft IPacket packet; while (!client.PacketQueue.TryDequeue(out packet)) { } PacketReader.WritePacket(client.MinecraftStream, packet); + Console.WriteLine("Sent {0}", packet.GetType().Name); if (packet is DisconnectPacket) { DisconnectClient(client); @@ -114,6 +130,7 @@ namespace TrueCraft if (client.DataAvailable) { var packet = PacketReader.ReadPacket(client.MinecraftStream); + Console.WriteLine("Got {0}", packet.GetType().Name); if (PacketHandlers[packet.ID] != null) { try diff --git a/TrueCraft/Program.cs b/TrueCraft/Program.cs index b02fa7c..b929adc 100644 --- a/TrueCraft/Program.cs +++ b/TrueCraft/Program.cs @@ -4,6 +4,7 @@ using System.Threading; using TrueCraft.Core.World; using TrueCraft.Core.TerrainGen; using TrueCraft.Core.Logging; +using TrueCraft.API.Logging; namespace TrueCraft { @@ -14,7 +15,7 @@ namespace TrueCraft // TODO: Make this more flexible var server = new MultiplayerServer(); server.AddWorld(new World("default", new FlatlandGenerator())); - server.AddLogProvider(new ConsoleLogProvider()); + server.AddLogProvider(new ConsoleLogProvider(LogCategory.All)); server.Start(new IPEndPoint(IPAddress.Any, 25565)); while (true) Thread.Sleep(1000); diff --git a/TrueCraft/RemoteClient.cs b/TrueCraft/RemoteClient.cs index 1d20fcc..d9585a2 100644 --- a/TrueCraft/RemoteClient.cs +++ b/TrueCraft/RemoteClient.cs @@ -3,16 +3,27 @@ using TrueCraft.API.Networking; using System.Net.Sockets; using TrueCraft.Core.Networking; using System.Collections.Concurrent; +using TrueCraft.API.Server; +using TrueCraft.API.World; +using TrueCraft.API.Entities; +using TrueCraft.API; +using System.Collections.Generic; +using System.Linq; +using TrueCraft.Core.Networking.Packets; +using TrueCraft.Core.World; +using Ionic.Zlib; namespace TrueCraft { public class RemoteClient : IRemoteClient { - public RemoteClient(NetworkStream stream) + public RemoteClient(IMultiplayerServer server, NetworkStream stream) { NetworkStream = stream; - MinecraftStream = new MinecraftStream(NetworkStream); + MinecraftStream = new MinecraftStream(new BufferedStream(NetworkStream)); PacketQueue = new ConcurrentQueue(); + LoadedChunks = new List(); + Server = server; } public NetworkStream NetworkStream { get; set; } @@ -20,6 +31,12 @@ namespace TrueCraft public ConcurrentQueue PacketQueue { get; private set; } public string Username { get; internal set; } public bool LoggedIn { get; internal set; } + public IMultiplayerServer Server { get; set; } + public IWorld World { get; internal set; } + public IEntity Entity { get; internal set; } + + internal int ChunkRadius { get; set; } + internal IList LoadedChunks { get; set; } public bool DataAvailable { @@ -33,5 +50,81 @@ namespace TrueCraft { PacketQueue.Enqueue(packet); } + + internal void UpdateChunks() + { + var newChunks = new List(); + for (int x = -ChunkRadius; x < ChunkRadius; x++) + { + for (int z = -ChunkRadius; z < ChunkRadius; z++) + { + newChunks.Add(new Coordinates2D( + ((int)Entity.Position.X >> 4) + x, + ((int)Entity.Position.Z >> 4) + z)); + } + } + // Unload extraneous columns + lock (LoadedChunks) + { + var currentChunks = new List(LoadedChunks); + foreach (Coordinates2D chunk in currentChunks) + { + if (!newChunks.Contains(chunk)) + UnloadChunk(chunk); + } + // Load new columns + foreach (Coordinates2D chunk in newChunks) + { + if (!LoadedChunks.Contains(chunk)) + LoadChunk(chunk); + } + } + } + + internal void UnloadAllChunks() + { + lock (LoadedChunks) + { + while (LoadedChunks.Any()) + { + UnloadChunk(LoadedChunks[0]); + } + } + } + + internal void LoadChunk(Coordinates2D position) + { + var chunk = World.GetChunk(position); + QueuePacket(new ChunkPreamblePacket(chunk.Coordinates.X, chunk.Coordinates.Z)); + QueuePacket(CreatePacket(chunk)); + LoadedChunks.Add(position); + } + + internal void UnloadChunk(Coordinates2D position) + { + QueuePacket(new ChunkPreamblePacket(position.X, position.Z, false)); + LoadedChunks.Remove(position); + } + + public static ChunkDataPacket CreatePacket(IChunk chunk) + { + // TODO: Be smarter about this + var X = chunk.Coordinates.X; + var Z = chunk.Coordinates.Z; + + const int blocksPerChunk = Chunk.Width * Chunk.Height * Chunk.Depth; + const int bytesPerChunk = (int)(blocksPerChunk * 2.5); + + byte[] data = new byte[bytesPerChunk]; + + Array.Copy(chunk.Blocks, 0, data, 0, chunk.Blocks.Length); + Array.Copy(chunk.Metadata.Data, 0, data, chunk.Blocks.Length, chunk.Metadata.Data.Length); + Array.Copy(chunk.BlockLight.Data, 0, data, chunk.Blocks.Length + chunk.Metadata.Data.Length, chunk.BlockLight.Data.Length); + Array.Copy(chunk.SkyLight.Data, 0, data, chunk.Blocks.Length + chunk.Metadata.Data.Length + + chunk.BlockLight.Data.Length, chunk.SkyLight.Data.Length); + + var result = ZlibStream.CompressBuffer(data); + return new ChunkDataPacket(X * Chunk.Width, 0, Z * Chunk.Depth, Chunk.Width, Chunk.Height, Chunk.Depth, result); + } } } \ No newline at end of file diff --git a/TrueCraft/TrueCraft.csproj b/TrueCraft/TrueCraft.csproj index 2e2c7d6..80ae192 100644 --- a/TrueCraft/TrueCraft.csproj +++ b/TrueCraft/TrueCraft.csproj @@ -30,6 +30,9 @@ + + ..\lib\Ionic.Zip.Reduced.dll + @@ -39,6 +42,8 @@ + + @@ -50,8 +55,13 @@ {FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D} TrueCraft.Core + + {4488498D-976D-4DA3-BF72-109531AF0488} + fNbt + + \ No newline at end of file diff --git a/doc/differences-from-vanilla b/doc/differences-from-vanilla index 8d3027b..986227d 100644 --- a/doc/differences-from-vanilla +++ b/doc/differences-from-vanilla @@ -1,3 +1,3 @@ Differences between TrueCraft and vanilla beta 1.7.3: -- Uses the Anvil level format instead of MCRegion +- Uses a modified form of the Anvil level format instead of MCRegion