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