Send initial slew of chunks to connected clients

This commit is contained in:
Drew DeVault 2014-12-27 18:19:42 -07:00
parent 05e132850c
commit 4d3d5ee8e0
27 changed files with 424 additions and 117 deletions

View File

@ -0,0 +1,10 @@
using System;
namespace TrueCraft.API.Entities
{
public interface IEntity
{
int EntityID { get; set; }
Vector3 Position { get; set; }
}
}

View File

@ -25,5 +25,6 @@ namespace TrueCraft.API.Logging
/// Generally useful information. /// Generally useful information.
/// </summary> /// </summary>
Notice = 16, Notice = 16,
All = Packets | Debug | Warning | Error | Notice
} }
} }

View File

@ -5,6 +5,8 @@ namespace TrueCraft.API.Networking
{ {
public interface IMinecraftStream public interface IMinecraftStream
{ {
Stream BaseStream { get; }
byte ReadUInt8(); byte ReadUInt8();
sbyte ReadInt8(); sbyte ReadInt8();
void WriteUInt8(byte value); void WriteUInt8(byte value);

View File

@ -1,4 +1,6 @@
using System; using System;
using TrueCraft.API.World;
using TrueCraft.API.Entities;
namespace TrueCraft.API.Networking namespace TrueCraft.API.Networking
{ {
@ -6,6 +8,8 @@ namespace TrueCraft.API.Networking
{ {
IMinecraftStream MinecraftStream { get; } IMinecraftStream MinecraftStream { get; }
bool DataAvailable { get; } bool DataAvailable { get; }
IWorld World { get; }
IEntity Entity { get; }
void QueuePacket(IPacket packet); void QueuePacket(IPacket packet);
} }

View File

@ -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
{
/// <summary>
/// Adds an entity to the world and assigns it an entity ID.
/// </summary>
void SpawnEntity(IEntity entity);
void DespawnEntity(IEntity entity);
IEntity GetEntityByID(int id);
void Update();
void SendEntitiesToClient(IRemoteClient client);
}
}

View File

@ -25,5 +25,6 @@ namespace TrueCraft.API.Server
void AddWorld(IWorld world); void AddWorld(IWorld world);
void AddLogProvider(ILogProvider provider); void AddLogProvider(ILogProvider provider);
void Log(LogCategory category, string text, params object[] parameters); void Log(LogCategory category, string text, params object[] parameters);
IEntityManager GetEntityManagerForWorld(IWorld world);
} }
} }

View File

@ -56,13 +56,14 @@
<Compile Include="World\IWorld.cs" /> <Compile Include="World\IWorld.cs" />
<Compile Include="World\IChunk.cs" /> <Compile Include="World\IChunk.cs" />
<Compile Include="World\IChunkProvider.cs" /> <Compile Include="World\IChunkProvider.cs" />
<Compile Include="World\ISection.cs" />
<Compile Include="NibbleArray.cs" /> <Compile Include="NibbleArray.cs" />
<Compile Include="World\IRegion.cs" /> <Compile Include="World\IRegion.cs" />
<Compile Include="Biome.cs" /> <Compile Include="Biome.cs" />
<Compile Include="Logging\LogCategory.cs" /> <Compile Include="Logging\LogCategory.cs" />
<Compile Include="Logging\ILogProvider.cs" /> <Compile Include="Logging\ILogProvider.cs" />
<Compile Include="Server\IEventScheduler.cs" /> <Compile Include="Server\IEventScheduler.cs" />
<Compile Include="Server\IEntityManager.cs" />
<Compile Include="Entities\IEntity.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup> <ItemGroup>
@ -70,6 +71,7 @@
<Folder Include="Server\" /> <Folder Include="Server\" />
<Folder Include="World\" /> <Folder Include="World\" />
<Folder Include="Logging\" /> <Folder Include="Logging\" />
<Folder Include="Entities\" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\externals\fNbt\fNbt\fNbt.csproj"> <ProjectReference Include="..\externals\fNbt\fNbt\fNbt.csproj">

View File

@ -7,9 +7,12 @@ namespace TrueCraft.API.World
Coordinates2D Coordinates { get; set; } Coordinates2D Coordinates { get; set; }
bool IsModified { get; set; } bool IsModified { get; set; }
int[] HeightMap { get; } int[] HeightMap { get; }
ISection[] Sections { get; }
byte[] Biomes { get; } byte[] Biomes { get; }
DateTime LastAccessed { get; set; } DateTime LastAccessed { get; set; }
byte[] Blocks { get; }
NibbleArray Metadata { get; }
NibbleArray BlockLight { get; }
NibbleArray SkyLight { get; }
byte GetBlockID(Coordinates3D coordinates); byte GetBlockID(Coordinates3D coordinates);
byte GetMetadata(Coordinates3D coordinates); byte GetMetadata(Coordinates3D coordinates);
byte GetSkyLight(Coordinates3D coordinates); byte GetSkyLight(Coordinates3D coordinates);

View File

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

View File

@ -0,0 +1,77 @@
using System;
using System.IO;
namespace TrueCraft.Core.Networking
{
/// <summary>
/// Queues all writes until Stream.Flush() is called. This is different than System.IO.BufferedStream.
/// </summary>
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; }
/// <summary>
/// Used by PacketReader to insert the ID and length into the stream before the packet contents.
/// </summary>
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);
}
}
}

View File

@ -32,13 +32,13 @@ namespace TrueCraft.Core.Networking
RegisterPacketType<PlayerGroundedPacket>(serverbound: true, clientbound: false); // 0x0A RegisterPacketType<PlayerGroundedPacket>(serverbound: true, clientbound: false); // 0x0A
RegisterPacketType<PlayerPositionPacket>(serverbound: true, clientbound: false); // 0x0B RegisterPacketType<PlayerPositionPacket>(serverbound: true, clientbound: false); // 0x0B
RegisterPacketType<PlayerLookPacket>(serverbound: true, clientbound: false); // 0x0C RegisterPacketType<PlayerLookPacket>(serverbound: true, clientbound: false); // 0x0C
RegisterPacketType<PlayerPositionPacket>(serverbound: true, clientbound: false); // 0x0D RegisterPacketType<PlayerPositionAndLookPacket>(serverbound: true, clientbound: false); // 0x0D
RegisterPacketType<SetPlayerPositionPacket>(serverbound: false, clientbound: true); // 0x0D RegisterPacketType<SetPlayerPositionPacket>(serverbound: false, clientbound: true); // 0x0D
RegisterPacketType<PlayerDiggingPacket>(serverbound: true, clientbound: false); // 0x0E RegisterPacketType<PlayerDiggingPacket>(serverbound: true, clientbound: false); // 0x0E
RegisterPacketType<PlayerBlockPlacementPacket>(serverbound: true, clientbound: false); // 0x0F RegisterPacketType<PlayerBlockPlacementPacket>(serverbound: true, clientbound: false); // 0x0F
RegisterPacketType<ChangeHeldItemPacket>(serverbound: true, clientbound: false); // 0x10 RegisterPacketType<ChangeHeldItemPacket>(serverbound: true, clientbound: false); // 0x10
RegisterPacketType<UseBedPacket>(serverbound: false, clientbound: true); // 0x11 RegisterPacketType<UseBedPacket>(serverbound: false, clientbound: true); // 0x11
RegisterPacketType<AnimationPacket>(serverbound: false, clientbound: true); // 0x12 RegisterPacketType<AnimationPacket>(); // 0x12
RegisterPacketType<PlayerActionPacket>(serverbound: true, clientbound: false); // 0x13 RegisterPacketType<PlayerActionPacket>(serverbound: true, clientbound: false); // 0x13
RegisterPacketType<SpawnPlayerPacket>(serverbound: false, clientbound: true); // 0x14 RegisterPacketType<SpawnPlayerPacket>(serverbound: false, clientbound: true); // 0x14
RegisterPacketType<SpawnItemPacket>(serverbound: false, clientbound: true); // 0x15 RegisterPacketType<SpawnItemPacket>(serverbound: false, clientbound: true); // 0x15
@ -114,6 +114,7 @@ namespace TrueCraft.Core.Networking
{ {
stream.WriteUInt8(packet.ID); stream.WriteUInt8(packet.ID);
packet.WritePacket(stream); packet.WritePacket(stream);
stream.BaseStream.Flush();
} }
} }
} }

View File

@ -12,6 +12,11 @@ namespace TrueCraft.Core.Networking.Packets
{ {
public byte ID { get { return 0x03; } } public byte ID { get { return 0x03; } }
public ChatMessagePacket(string message)
{
Message = message;
}
public string Message; public string Message;
public void ReadPacket(IMinecraftStream stream) public void ReadPacket(IMinecraftStream stream)

View File

@ -10,6 +10,17 @@ namespace TrueCraft.Core.Networking.Packets
{ {
public byte ID { get { return 0x33; } } 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 int X;
public short Y; public short Y;
public int Z; public int Z;

View File

@ -10,6 +10,13 @@ namespace TrueCraft.Core.Networking.Packets
{ {
public byte ID { get { return 0x32; } } 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; public int X, Z;
/// <summary> /// <summary>
/// If false, free the chunk. If true, allocate it. /// If false, free the chunk. If true, allocate it.

View File

@ -11,6 +11,17 @@ namespace TrueCraft.Core.Networking.Packets
{ {
public byte ID { get { return 0x0D; } } 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 X, Y, Z;
public double Stance; public double Stance;
public float Yaw, Pitch; public float Yaw, Pitch;
@ -33,8 +44,8 @@ namespace TrueCraft.Core.Networking.Packets
stream.WriteDouble(Y); stream.WriteDouble(Y);
stream.WriteDouble(Stance); stream.WriteDouble(Stance);
stream.WriteDouble(Z); stream.WriteDouble(Z);
stream.WriteDouble(Yaw); stream.WriteSingle(Yaw);
stream.WriteDouble(Pitch); stream.WriteSingle(Pitch);
stream.WriteBoolean(OnGround); stream.WriteBoolean(OnGround);
} }
} }

View File

@ -10,6 +10,17 @@ namespace TrueCraft.Core.Networking.Packets
{ {
public byte ID { get { return 0x0D; } } 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 X, Y, Z;
public double Stance; public double Stance;
public float Yaw, Pitch; public float Yaw, Pitch;
@ -32,8 +43,8 @@ namespace TrueCraft.Core.Networking.Packets
stream.WriteDouble(Stance); stream.WriteDouble(Stance);
stream.WriteDouble(Y); stream.WriteDouble(Y);
stream.WriteDouble(Z); stream.WriteDouble(Z);
stream.WriteDouble(Yaw); stream.WriteSingle(Yaw);
stream.WriteDouble(Pitch); stream.WriteSingle(Pitch);
stream.WriteBoolean(OnGround); stream.WriteBoolean(OnGround);
} }
} }

View File

@ -102,6 +102,7 @@
<Compile Include="World\World.cs" /> <Compile Include="World\World.cs" />
<Compile Include="TerrainGen\FlatlandGenerator.cs" /> <Compile Include="TerrainGen\FlatlandGenerator.cs" />
<Compile Include="Logging\ConsoleLogProvider.cs" /> <Compile Include="Logging\ConsoleLogProvider.cs" />
<Compile Include="Networking\BufferedStream.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup> <ItemGroup>

View File

@ -12,25 +12,26 @@ namespace TrueCraft.Core.World
{ {
public class Chunk : INbtSerializable, IChunk 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)); private static readonly NbtSerializer Serializer = new NbtSerializer(typeof(Chunk));
[NbtIgnore] [NbtIgnore]
public DateTime LastAccessed { get; set; } public DateTime LastAccessed { get; set; }
public bool IsModified { get; set; }
public byte[] Biomes { get; set; }
public int[] HeightMap { get; set; }
[NbtIgnore] [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")] [TagName("xPos")]
public int X { get; set; } public int X { get; set; }
[TagName("zPos")] [TagName("zPos")]
public int Z { get; set; } public int Z { get; set; }
@ -57,9 +58,6 @@ namespace TrueCraft.Core.World
public Chunk() public Chunk()
{ {
TerrainPopulated = true; 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]; Biomes = new byte[Width * Depth];
HeightMap = new int[Width * Depth]; HeightMap = new int[Width * Depth];
LastAccessed = DateTime.Now; LastAccessed = DateTime.Now;
@ -69,47 +67,49 @@ namespace TrueCraft.Core.World
{ {
X = coordinates.X; X = coordinates.X;
Z = coordinates.Z; 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) public byte GetBlockID(Coordinates3D coordinates)
{ {
LastAccessed = DateTime.Now; LastAccessed = DateTime.Now;
int section = GetSectionNumber(coordinates.Y); int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width);
coordinates.Y = GetPositionInSection(coordinates.Y); return Blocks[index];
return Sections[section].GetBlockID(coordinates);
} }
public byte GetMetadata(Coordinates3D coordinates) public byte GetMetadata(Coordinates3D coordinates)
{ {
LastAccessed = DateTime.Now; LastAccessed = DateTime.Now;
int section = GetSectionNumber(coordinates.Y); int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width);
coordinates.Y = GetPositionInSection(coordinates.Y); return Metadata[index];
return Sections[section].GetMetadata(coordinates);
} }
public byte GetSkyLight(Coordinates3D coordinates) public byte GetSkyLight(Coordinates3D coordinates)
{ {
LastAccessed = DateTime.Now; LastAccessed = DateTime.Now;
int section = GetSectionNumber(coordinates.Y); int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width);
coordinates.Y = GetPositionInSection(coordinates.Y); return SkyLight[index];
return Sections[section].GetSkyLight(coordinates);
} }
public byte GetBlockLight(Coordinates3D coordinates) public byte GetBlockLight(Coordinates3D coordinates)
{ {
LastAccessed = DateTime.Now; LastAccessed = DateTime.Now;
int section = GetSectionNumber(coordinates.Y); int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width);
coordinates.Y = GetPositionInSection(coordinates.Y); return BlockLight[index];
return Sections[section].GetBlockLight(coordinates);
} }
public void SetBlockID(Coordinates3D coordinates, byte value) public void SetBlockID(Coordinates3D coordinates, byte value)
{ {
LastAccessed = DateTime.Now; LastAccessed = DateTime.Now;
IsModified = true; IsModified = true;
int section = GetSectionNumber(coordinates.Y); int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width);
coordinates.Y = GetPositionInSection(coordinates.Y); Blocks[index] = value;
Sections[section].SetBlockID(coordinates, value);
var oldHeight = GetHeight((byte)coordinates.X, (byte)coordinates.Z); var oldHeight = GetHeight((byte)coordinates.X, (byte)coordinates.Z);
if (value == 0) // Air if (value == 0) // Air
{ {
@ -135,37 +135,24 @@ namespace TrueCraft.Core.World
{ {
LastAccessed = DateTime.Now; LastAccessed = DateTime.Now;
IsModified = true; IsModified = true;
int section = GetSectionNumber(coordinates.Y); int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width);
coordinates.Y = GetPositionInSection(coordinates.Y); Metadata[index] = value;
Sections[section].SetMetadata(coordinates, value);
} }
public void SetSkyLight(Coordinates3D coordinates, byte value) public void SetSkyLight(Coordinates3D coordinates, byte value)
{ {
LastAccessed = DateTime.Now; LastAccessed = DateTime.Now;
IsModified = true; IsModified = true;
int section = GetSectionNumber(coordinates.Y); int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width);
coordinates.Y = GetPositionInSection(coordinates.Y); SkyLight[index] = value;
Sections[section].SetSkyLight(coordinates, value);
} }
public void SetBlockLight(Coordinates3D coordinates, byte value) public void SetBlockLight(Coordinates3D coordinates, byte value)
{ {
LastAccessed = DateTime.Now; LastAccessed = DateTime.Now;
IsModified = true; IsModified = true;
int section = GetSectionNumber(coordinates.Y); int index = coordinates.Y + (coordinates.Z * Height) + (coordinates.X * Height * Width);
coordinates.Y = GetPositionInSection(coordinates.Y); BlockLight[index] = value;
Sections[section].SetBlockLight(coordinates, value);
}
private static int GetSectionNumber(int yPos)
{
return yPos / 16;
}
private static int GetPositionInSection(int yPos)
{
return yPos % 16;
} }
/// <summary> /// <summary>
@ -206,19 +193,7 @@ namespace TrueCraft.Core.World
var chunk = (NbtCompound)Serializer.Serialize(this, tagName, true); var chunk = (NbtCompound)Serializer.Serialize(this, tagName, true);
var entities = new NbtList("Entities", NbtTagType.Compound); var entities = new NbtList("Entities", NbtTagType.Compound);
chunk.Add(entities); chunk.Add(entities);
var sections = new NbtList("Sections", NbtTagType.Compound); // TODO: Save block data
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);
return chunk; return chunk;
} }
@ -231,18 +206,11 @@ namespace TrueCraft.Core.World
this.Biomes = chunk.Biomes; this.Biomes = chunk.Biomes;
this.HeightMap = chunk.HeightMap; this.HeightMap = chunk.HeightMap;
this.LastUpdate = chunk.LastUpdate; this.LastUpdate = chunk.LastUpdate;
this.Sections = chunk.Sections;
this.TerrainPopulated = chunk.TerrainPopulated; this.TerrainPopulated = chunk.TerrainPopulated;
this.X = chunk.X; this.X = chunk.X;
this.Z = chunk.Z; this.Z = chunk.Z;
var serializer = new NbtSerializer(typeof(Section)); // TODO: Load block data
foreach (var section in compound["Sections"] as NbtList)
{
int index = section["Y"].IntValue;
Sections[index] = (Section)serializer.Deserialize(section);
Sections[index].ProcessSection();
}
} }
} }
} }

View File

@ -4,7 +4,7 @@ using TrueCraft.API.World;
namespace TrueCraft.Core.World namespace TrueCraft.Core.World
{ {
public class Section : ISection public class Section
{ {
public const byte Width = 16, Height = 16, Depth = 16; public const byte Width = 16, Height = 16, Depth = 16;
@ -42,31 +42,31 @@ namespace TrueCraft.Core.World
public byte GetBlockID(Coordinates3D coordinates) 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]; return Blocks[index];
} }
public byte GetMetadata(Coordinates3D coordinates) 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]; return Metadata[index];
} }
public byte GetSkyLight(Coordinates3D coordinates) 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]; return SkyLight[index];
} }
public byte GetBlockLight(Coordinates3D coordinates) 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]; return BlockLight[index];
} }
public void SetBlockID(Coordinates3D coordinates, byte value) 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 (value == 0)
{ {
if (Blocks[index] != 0) if (Blocks[index] != 0)
@ -82,19 +82,19 @@ namespace TrueCraft.Core.World
public void SetMetadata(Coordinates3D coordinates, byte value) 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; Metadata[index] = value;
} }
public void SetSkyLight(Coordinates3D coordinates, byte 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; SkyLight[index] = value;
} }
public void SetBlockLight(Coordinates3D coordinates, byte 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; BlockLight[index] = value;
} }

View File

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

View File

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

View File

@ -3,6 +3,8 @@ using TrueCraft.API.Server;
using TrueCraft.API.Networking; using TrueCraft.API.Networking;
using TrueCraft.Core.Networking.Packets; using TrueCraft.Core.Networking.Packets;
using TrueCraft.API.Logging; using TrueCraft.API.Logging;
using TrueCraft.API;
using TrueCraft.Entities;
namespace TrueCraft.Handlers namespace TrueCraft.Handlers
{ {
@ -21,16 +23,24 @@ namespace TrueCraft.Handlers
var packet = (LoginRequestPacket)_packet; var packet = (LoginRequestPacket)_packet;
var client = (RemoteClient)_client; var client = (RemoteClient)_client;
if (packet.ProtocolVersion < server.PacketReader.ProtocolVersion) 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) 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) else if (server.Worlds.Count == 0)
client.QueuePacket(new DisconnectPacket("Server has no worlds configured.")); client.QueuePacket(new DisconnectPacket("Server has no worlds configured."));
else 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; 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)));
} }
} }
} }

View File

@ -17,6 +17,7 @@ namespace TrueCraft
public IPacketReader PacketReader { get; private set; } public IPacketReader PacketReader { get; private set; }
public IList<IRemoteClient> Clients { get; private set; } public IList<IRemoteClient> Clients { get; private set; }
public IList<IWorld> Worlds { get; private set; } public IList<IWorld> Worlds { get; private set; }
public IList<IEntityManager> EntityManagers { get; private set; }
public IEventScheduler Scheduler { get; private set; } public IEventScheduler Scheduler { get; private set; }
private Timer NetworkWorker, EnvironmentWorker; private Timer NetworkWorker, EnvironmentWorker;
@ -33,6 +34,7 @@ namespace TrueCraft
EnvironmentWorker = new Timer(DoEnvironment); EnvironmentWorker = new Timer(DoEnvironment);
PacketHandlers = new PacketHandler[0x100]; PacketHandlers = new PacketHandler[0x100];
Worlds = new List<IWorld>(); Worlds = new List<IWorld>();
EntityManagers = new List<IEntityManager>();
LogProviders = new List<ILogProvider>(); LogProviders = new List<ILogProvider>();
Scheduler = new EventScheduler(this); Scheduler = new EventScheduler(this);
@ -58,6 +60,8 @@ namespace TrueCraft
public void AddWorld(IWorld world) public void AddWorld(IWorld world)
{ {
Worlds.Add(world); Worlds.Add(world);
var manager = new EntityManager(this, world);
EntityManagers.Add(manager);
} }
public void AddLogProvider(ILogProvider provider) 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) private void DisconnectClient(IRemoteClient _client)
{ {
var client = (RemoteClient)_client; var client = (RemoteClient)_client;
@ -85,7 +100,7 @@ namespace TrueCraft
private void AcceptClient(IAsyncResult result) private void AcceptClient(IAsyncResult result)
{ {
var tcpClient = Listener.EndAcceptTcpClient(result); var tcpClient = Listener.EndAcceptTcpClient(result);
var client = new RemoteClient(tcpClient.GetStream()); var client = new RemoteClient(this, tcpClient.GetStream());
Clients.Add(client); Clients.Add(client);
Listener.BeginAcceptTcpClient(AcceptClient, null); Listener.BeginAcceptTcpClient(AcceptClient, null);
} }
@ -105,6 +120,7 @@ namespace TrueCraft
IPacket packet; IPacket packet;
while (!client.PacketQueue.TryDequeue(out packet)) { } while (!client.PacketQueue.TryDequeue(out packet)) { }
PacketReader.WritePacket(client.MinecraftStream, packet); PacketReader.WritePacket(client.MinecraftStream, packet);
Console.WriteLine("Sent {0}", packet.GetType().Name);
if (packet is DisconnectPacket) if (packet is DisconnectPacket)
{ {
DisconnectClient(client); DisconnectClient(client);
@ -114,6 +130,7 @@ namespace TrueCraft
if (client.DataAvailable) if (client.DataAvailable)
{ {
var packet = PacketReader.ReadPacket(client.MinecraftStream); var packet = PacketReader.ReadPacket(client.MinecraftStream);
Console.WriteLine("Got {0}", packet.GetType().Name);
if (PacketHandlers[packet.ID] != null) if (PacketHandlers[packet.ID] != null)
{ {
try try

View File

@ -4,6 +4,7 @@ using System.Threading;
using TrueCraft.Core.World; using TrueCraft.Core.World;
using TrueCraft.Core.TerrainGen; using TrueCraft.Core.TerrainGen;
using TrueCraft.Core.Logging; using TrueCraft.Core.Logging;
using TrueCraft.API.Logging;
namespace TrueCraft namespace TrueCraft
{ {
@ -14,7 +15,7 @@ namespace TrueCraft
// TODO: Make this more flexible // TODO: Make this more flexible
var server = new MultiplayerServer(); var server = new MultiplayerServer();
server.AddWorld(new World("default", new FlatlandGenerator())); server.AddWorld(new World("default", new FlatlandGenerator()));
server.AddLogProvider(new ConsoleLogProvider()); server.AddLogProvider(new ConsoleLogProvider(LogCategory.All));
server.Start(new IPEndPoint(IPAddress.Any, 25565)); server.Start(new IPEndPoint(IPAddress.Any, 25565));
while (true) while (true)
Thread.Sleep(1000); Thread.Sleep(1000);

View File

@ -3,16 +3,27 @@ using TrueCraft.API.Networking;
using System.Net.Sockets; using System.Net.Sockets;
using TrueCraft.Core.Networking; using TrueCraft.Core.Networking;
using System.Collections.Concurrent; 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 namespace TrueCraft
{ {
public class RemoteClient : IRemoteClient public class RemoteClient : IRemoteClient
{ {
public RemoteClient(NetworkStream stream) public RemoteClient(IMultiplayerServer server, NetworkStream stream)
{ {
NetworkStream = stream; NetworkStream = stream;
MinecraftStream = new MinecraftStream(NetworkStream); MinecraftStream = new MinecraftStream(new BufferedStream(NetworkStream));
PacketQueue = new ConcurrentQueue<IPacket>(); PacketQueue = new ConcurrentQueue<IPacket>();
LoadedChunks = new List<Coordinates2D>();
Server = server;
} }
public NetworkStream NetworkStream { get; set; } public NetworkStream NetworkStream { get; set; }
@ -20,6 +31,12 @@ namespace TrueCraft
public ConcurrentQueue<IPacket> PacketQueue { get; private set; } public ConcurrentQueue<IPacket> PacketQueue { get; private set; }
public string Username { get; internal set; } public string Username { get; internal set; }
public bool LoggedIn { 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<Coordinates2D> LoadedChunks { get; set; }
public bool DataAvailable public bool DataAvailable
{ {
@ -33,5 +50,81 @@ namespace TrueCraft
{ {
PacketQueue.Enqueue(packet); PacketQueue.Enqueue(packet);
} }
internal void UpdateChunks()
{
var newChunks = new List<Coordinates2D>();
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<Coordinates2D>(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);
}
} }
} }

View File

@ -30,6 +30,9 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="Ionic.Zip.Reduced">
<HintPath>..\lib\Ionic.Zip.Reduced.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
@ -39,6 +42,8 @@
<Compile Include="Handlers\PacketHandlers.cs" /> <Compile Include="Handlers\PacketHandlers.cs" />
<Compile Include="Handlers\LoginHandlers.cs" /> <Compile Include="Handlers\LoginHandlers.cs" />
<Compile Include="EventScheduler.cs" /> <Compile Include="EventScheduler.cs" />
<Compile Include="EntityManager.cs" />
<Compile Include="Entities\PlayerEntity.cs" />
</ItemGroup> </ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup> <ItemGroup>
@ -50,8 +55,13 @@
<Project>{FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}</Project> <Project>{FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}</Project>
<Name>TrueCraft.Core</Name> <Name>TrueCraft.Core</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\externals\fNbt\fNbt\fNbt.csproj">
<Project>{4488498D-976D-4DA3-BF72-109531AF0488}</Project>
<Name>fNbt</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Handlers\" /> <Folder Include="Handlers\" />
<Folder Include="Entities\" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,3 +1,3 @@
Differences between TrueCraft and vanilla beta 1.7.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