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.
/// </summary>
Notice = 16,
All = Packets | Debug | Warning | Error | Notice
}
}

View File

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

View File

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

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

View File

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

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<PlayerPositionPacket>(serverbound: true, clientbound: false); // 0x0B
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<PlayerDiggingPacket>(serverbound: true, clientbound: false); // 0x0E
RegisterPacketType<PlayerBlockPlacementPacket>(serverbound: true, clientbound: false); // 0x0F
RegisterPacketType<ChangeHeldItemPacket>(serverbound: true, clientbound: false); // 0x10
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<SpawnPlayerPacket>(serverbound: false, clientbound: true); // 0x14
RegisterPacketType<SpawnItemPacket>(serverbound: false, clientbound: true); // 0x15
@ -114,6 +114,7 @@ namespace TrueCraft.Core.Networking
{
stream.WriteUInt8(packet.ID);
packet.WritePacket(stream);
stream.BaseStream.Flush();
}
}
}

View File

@ -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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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.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)));
}
}
}

View File

@ -17,6 +17,7 @@ namespace TrueCraft
public IPacketReader PacketReader { get; private set; }
public IList<IRemoteClient> Clients { get; private set; }
public IList<IWorld> Worlds { get; private set; }
public IList<IEntityManager> 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<IWorld>();
EntityManagers = new List<IEntityManager>();
LogProviders = new List<ILogProvider>();
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

View File

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

View File

@ -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<IPacket>();
LoadedChunks = new List<Coordinates2D>();
Server = server;
}
public NetworkStream NetworkStream { get; set; }
@ -20,6 +31,12 @@ namespace TrueCraft
public ConcurrentQueue<IPacket> 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<Coordinates2D> LoadedChunks { get; set; }
public bool DataAvailable
{
@ -33,5 +50,81 @@ namespace TrueCraft
{
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>
<ItemGroup>
<Reference Include="System" />
<Reference Include="Ionic.Zip.Reduced">
<HintPath>..\lib\Ionic.Zip.Reduced.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
@ -39,6 +42,8 @@
<Compile Include="Handlers\PacketHandlers.cs" />
<Compile Include="Handlers\LoginHandlers.cs" />
<Compile Include="EventScheduler.cs" />
<Compile Include="EntityManager.cs" />
<Compile Include="Entities\PlayerEntity.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
@ -50,8 +55,13 @@
<Project>{FA4BE9A3-DBF0-4380-BA2B-FFAA71C4706D}</Project>
<Name>TrueCraft.Core</Name>
</ProjectReference>
<ProjectReference Include="..\externals\fNbt\fNbt\fNbt.csproj">
<Project>{4488498D-976D-4DA3-BF72-109531AF0488}</Project>
<Name>fNbt</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Handlers\" />
<Folder Include="Entities\" />
</ItemGroup>
</Project>

View File

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