Send initial slew of chunks to connected clients
This commit is contained in:
parent
05e132850c
commit
4d3d5ee8e0
10
TrueCraft.API/Entities/IEntity.cs
Normal file
10
TrueCraft.API/Entities/IEntity.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
|
||||
namespace TrueCraft.API.Entities
|
||||
{
|
||||
public interface IEntity
|
||||
{
|
||||
int EntityID { get; set; }
|
||||
Vector3 Position { get; set; }
|
||||
}
|
||||
}
|
@ -25,5 +25,6 @@ namespace TrueCraft.API.Logging
|
||||
/// Generally useful information.
|
||||
/// </summary>
|
||||
Notice = 16,
|
||||
All = Packets | Debug | Warning | Error | Notice
|
||||
}
|
||||
}
|
@ -5,6 +5,8 @@ namespace TrueCraft.API.Networking
|
||||
{
|
||||
public interface IMinecraftStream
|
||||
{
|
||||
Stream BaseStream { get; }
|
||||
|
||||
byte ReadUInt8();
|
||||
sbyte ReadInt8();
|
||||
void WriteUInt8(byte value);
|
||||
|
@ -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);
|
||||
}
|
||||
|
19
TrueCraft.API/Server/IEntityManager.cs
Normal file
19
TrueCraft.API/Server/IEntityManager.cs
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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">
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
77
TrueCraft.Core/Networking/BufferedStream.cs
Normal file
77
TrueCraft.Core/Networking/BufferedStream.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
16
TrueCraft/Entities/PlayerEntity.cs
Normal file
16
TrueCraft/Entities/PlayerEntity.cs
Normal 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; }
|
||||
}
|
||||
}
|
48
TrueCraft/EntityManager.cs
Normal file
48
TrueCraft/EntityManager.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user