Add chunk handling and stub out chunk worker
The chunk worker will be converting incoming chunks to renderable meshes.
This commit is contained in:
parent
4ec032892b
commit
756a309f8b
55
TrueCraft.Client.Linux/ChunkConverter.cs
Normal file
55
TrueCraft.Client.Linux/ChunkConverter.cs
Normal file
@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Threading;
|
||||
using System.Linq;
|
||||
|
||||
namespace TrueCraft.Client.Linux
|
||||
{
|
||||
/// <summary>
|
||||
/// A daemon of sorts that creates meshes from chunks.
|
||||
/// Passing meshes back is NOT thread-safe.
|
||||
/// </summary>
|
||||
public class ChunkConverter
|
||||
{
|
||||
private ConcurrentQueue<ReadOnlyChunk> ChunkQueue { get; set; }
|
||||
private Thread ChunkWorker { get; set; }
|
||||
|
||||
public ChunkConverter()
|
||||
{
|
||||
ChunkQueue = new ConcurrentQueue<ReadOnlyChunk>();
|
||||
ChunkWorker = new Thread(new ThreadStart(DoChunks));
|
||||
}
|
||||
|
||||
public void QueueChunk(ReadOnlyChunk chunk)
|
||||
{
|
||||
ChunkQueue.Enqueue(chunk);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
ChunkWorker.Start();
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
ChunkWorker.Abort();
|
||||
}
|
||||
|
||||
private void DoChunks()
|
||||
{
|
||||
bool idle = true;
|
||||
while (true)
|
||||
{
|
||||
ReadOnlyChunk chunk;
|
||||
if (ChunkQueue.Any())
|
||||
{
|
||||
while (!ChunkQueue.TryDequeue(out chunk)) { }
|
||||
// TODO: Create verticies from chunk
|
||||
Console.WriteLine("Chunk worker received chunk at {0}, {1}", chunk.X, chunk.Z);
|
||||
}
|
||||
if (idle)
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
14
TrueCraft.Client.Linux/Events/ChunkEventArgs.cs
Normal file
14
TrueCraft.Client.Linux/Events/ChunkEventArgs.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace TrueCraft.Client.Linux.Events
|
||||
{
|
||||
public class ChunkEventArgs
|
||||
{
|
||||
public ReadOnlyChunk Chunk { get; set; }
|
||||
|
||||
public ChunkEventArgs(ReadOnlyChunk chunk)
|
||||
{
|
||||
Chunk = chunk;
|
||||
}
|
||||
}
|
||||
}
|
35
TrueCraft.Client.Linux/Handlers/ChunkHandler.cs
Normal file
35
TrueCraft.Client.Linux/Handlers/ChunkHandler.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using TrueCraft.API.Networking;
|
||||
using TrueCraft.Core.Networking.Packets;
|
||||
using TrueCraft.API;
|
||||
using TrueCraft.Core.World;
|
||||
using MonoGame.Utilities;
|
||||
using TrueCraft.Client.Linux.Events;
|
||||
|
||||
namespace TrueCraft.Client.Linux.Handlers
|
||||
{
|
||||
internal static class ChunkHandler
|
||||
{
|
||||
public static void HandleChunkPreamble(IPacket _packet, MultiplayerClient client)
|
||||
{
|
||||
var packet = (ChunkPreamblePacket)_packet;
|
||||
var coords = new Coordinates2D(packet.X, packet.Z);
|
||||
client.World.SetChunk(coords, new Chunk(coords));
|
||||
}
|
||||
|
||||
public static void HandleChunkData(IPacket _packet, MultiplayerClient client)
|
||||
{
|
||||
var packet = (ChunkDataPacket)_packet;
|
||||
// TODO: Handle chunk data packets that only include a partial chunk
|
||||
// This only works with TrueCraft servers unless we fix that
|
||||
var data = ZlibStream.UncompressBuffer(packet.CompressedData);
|
||||
var chunk = client.World.FindChunk(new Coordinates2D(packet.X, packet.Z));
|
||||
|
||||
Array.Copy(data, 0, chunk.Blocks, 0, chunk.Blocks.Length);
|
||||
Array.Copy(data, chunk.Blocks.Length, chunk.Metadata.Data, 0, chunk.Metadata.Data.Length);
|
||||
// TODO: Light
|
||||
|
||||
client.OnChunkLoaded(new ChunkEventArgs(new ReadOnlyChunk(chunk)));
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,9 @@ namespace TrueCraft.Client.Linux.Handlers
|
||||
{
|
||||
client.RegisterPacketHandler(new HandshakeResponsePacket().ID, HandleHandshake);
|
||||
client.RegisterPacketHandler(new ChatMessagePacket().ID, HandleChatMessage);
|
||||
|
||||
client.RegisterPacketHandler(new ChunkPreamblePacket().ID, ChunkHandler.HandleChunkPreamble);
|
||||
client.RegisterPacketHandler(new ChunkDataPacket().ID, ChunkHandler.HandleChunkData);
|
||||
}
|
||||
|
||||
public static void HandleChatMessage(IPacket _packet, MultiplayerClient client)
|
||||
|
@ -19,6 +19,10 @@ namespace TrueCraft.Client.Linux
|
||||
public class MultiplayerClient
|
||||
{
|
||||
public event EventHandler<ChatMessageEventArgs> ChatMessage;
|
||||
public event EventHandler<ChunkEventArgs> ChunkLoaded;
|
||||
public event EventHandler<ChunkEventArgs> ChunkUnloaded;
|
||||
|
||||
public ReadOnlyWorld World { get; private set; }
|
||||
|
||||
private TcpClient Client { get; set; }
|
||||
private IMinecraftStream Stream { get; set; }
|
||||
@ -36,6 +40,7 @@ namespace TrueCraft.Client.Linux
|
||||
NetworkWorker = new Thread(new ThreadStart(DoNetwork));
|
||||
PacketHandlers = new PacketHandler[0x100];
|
||||
Handlers.PacketHandlers.RegisterHandlers(this);
|
||||
World = new ReadOnlyWorld();
|
||||
}
|
||||
|
||||
public void RegisterPacketHandler(byte packetId, PacketHandler handler)
|
||||
@ -67,14 +72,16 @@ namespace TrueCraft.Client.Linux
|
||||
while (true)
|
||||
{
|
||||
IPacket packet;
|
||||
while (Client.Available != 0)
|
||||
DateTime limit = DateTime.Now.AddMilliseconds(500);
|
||||
while (Client.Available != 0 && DateTime.Now < limit)
|
||||
{
|
||||
idle = false;
|
||||
packet = PacketReader.ReadPacket(Stream, false);
|
||||
if (PacketHandlers[packet.ID] != null)
|
||||
PacketHandlers[packet.ID](packet, this);
|
||||
}
|
||||
while (PacketQueue.Any())
|
||||
limit = DateTime.Now.AddMilliseconds(500);
|
||||
while (PacketQueue.Any() && DateTime.Now < limit)
|
||||
{
|
||||
idle = false;
|
||||
while (!PacketQueue.TryDequeue(out packet)) { }
|
||||
@ -90,5 +97,15 @@ namespace TrueCraft.Client.Linux
|
||||
{
|
||||
if (ChatMessage != null) ChatMessage(this, e);
|
||||
}
|
||||
|
||||
protected internal void OnChunkLoaded(ChunkEventArgs e)
|
||||
{
|
||||
if (ChunkLoaded != null) ChunkLoaded(this, e);
|
||||
}
|
||||
|
||||
protected internal void OnChunkUnloaded(ChunkEventArgs e)
|
||||
{
|
||||
if (ChunkUnloaded != null) ChunkUnloaded(this, e);
|
||||
}
|
||||
}
|
||||
}
|
@ -11,8 +11,7 @@ namespace TrueCraft.Client.Linux
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var client = new MultiplayerClient();
|
||||
client.Connect(ParseEndPoint(args[0])); // TODO: Connect after starting up the MonoGame instance
|
||||
var game = new TrueCraftGame(client);
|
||||
var game = new TrueCraftGame(client, ParseEndPoint(args[0]));
|
||||
game.Run();
|
||||
}
|
||||
|
||||
|
108
TrueCraft.Client.Linux/ReadOnlyWorld.cs
Normal file
108
TrueCraft.Client.Linux/ReadOnlyWorld.cs
Normal file
@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using TrueCraft.API.World;
|
||||
using TrueCraft.Core.World;
|
||||
using TrueCraft.API;
|
||||
|
||||
namespace TrueCraft.Client.Linux
|
||||
{
|
||||
public class ReadOnlyWorld
|
||||
{
|
||||
private bool UnloadChunks { get; set; }
|
||||
|
||||
internal World World { get; set; }
|
||||
|
||||
internal ReadOnlyWorld()
|
||||
{
|
||||
World = new World("default");
|
||||
UnloadChunks = true;
|
||||
}
|
||||
|
||||
public short GetBlockID(Coordinates3D coordinates)
|
||||
{
|
||||
return World.GetBlockID(coordinates);
|
||||
}
|
||||
|
||||
internal void SetBlockID(Coordinates3D coordinates, byte value)
|
||||
{
|
||||
World.SetBlockID(coordinates, value);
|
||||
}
|
||||
|
||||
internal void SetMetadata(Coordinates3D coordinates, byte value)
|
||||
{
|
||||
World.SetMetadata(coordinates, value);
|
||||
}
|
||||
|
||||
public byte GetMetadata(Coordinates3D coordinates)
|
||||
{
|
||||
return World.GetMetadata(coordinates);
|
||||
}
|
||||
|
||||
public byte GetSkyLight(Coordinates3D coordinates)
|
||||
{
|
||||
return World.GetSkyLight(coordinates);
|
||||
}
|
||||
|
||||
internal IChunk FindChunk(Coordinates2D coordinates)
|
||||
{
|
||||
return World.FindChunk(new Coordinates3D(coordinates.X, 0, coordinates.Z));
|
||||
}
|
||||
|
||||
public ReadOnlyChunk GetChunk(Coordinates2D coordinates)
|
||||
{
|
||||
return new ReadOnlyChunk(World.GetChunk(coordinates));
|
||||
}
|
||||
|
||||
internal void SetChunk(Coordinates2D coordinates, Chunk chunk)
|
||||
{
|
||||
World.SetChunk(coordinates, chunk);
|
||||
}
|
||||
|
||||
internal void RemoveChunk(Coordinates2D coordinates)
|
||||
{
|
||||
if (UnloadChunks)
|
||||
World.UnloadChunk(coordinates);
|
||||
}
|
||||
}
|
||||
|
||||
public class ReadOnlyChunk
|
||||
{
|
||||
internal IChunk Chunk { get; set; }
|
||||
|
||||
internal ReadOnlyChunk(IChunk chunk)
|
||||
{
|
||||
Chunk = chunk;
|
||||
}
|
||||
|
||||
public short GetBlockId(Coordinates3D coordinates)
|
||||
{
|
||||
return Chunk.GetBlockID(coordinates);
|
||||
}
|
||||
|
||||
public byte GetMetadata(Coordinates3D coordinates)
|
||||
{
|
||||
return Chunk.GetMetadata(coordinates);
|
||||
}
|
||||
|
||||
public byte GetSkyLight(Coordinates3D coordinates)
|
||||
{
|
||||
return Chunk.GetSkyLight(coordinates);
|
||||
}
|
||||
|
||||
public byte GetBlockLight(Coordinates3D coordinates)
|
||||
{
|
||||
return Chunk.GetBlockLight(coordinates);
|
||||
}
|
||||
|
||||
public int X { get { return Chunk.X; } }
|
||||
public int Z { get { return Chunk.Z; } }
|
||||
|
||||
public ReadOnlyCollection<byte> Blocks { get { return Array.AsReadOnly(Chunk.Blocks); } }
|
||||
public ReadOnlyNibbleArray Metadata { get { return new ReadOnlyNibbleArray(Chunk.Metadata); } }
|
||||
public ReadOnlyNibbleArray BlockLight { get { return new ReadOnlyNibbleArray(Chunk.BlockLight); } }
|
||||
public ReadOnlyNibbleArray SkyLight { get { return new ReadOnlyNibbleArray(Chunk.SkyLight); } }
|
||||
}
|
||||
}
|
@ -67,6 +67,10 @@
|
||||
<Compile Include="Interface\IGameInterface.cs" />
|
||||
<Compile Include="BMFont.cs" />
|
||||
<Compile Include="FontRenderer.cs" />
|
||||
<Compile Include="Handlers\ChunkHandler.cs" />
|
||||
<Compile Include="ReadOnlyWorld.cs" />
|
||||
<Compile Include="Events\ChunkEventArgs.cs" />
|
||||
<Compile Include="ChunkConverter.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
|
@ -5,6 +5,7 @@ using Microsoft.Xna.Framework.Graphics;
|
||||
using System.Collections.Generic;
|
||||
using TrueCraft.Client.Linux.Interface;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
|
||||
namespace TrueCraft.Client.Linux
|
||||
{
|
||||
@ -15,8 +16,10 @@ namespace TrueCraft.Client.Linux
|
||||
private List<IGameInterface> Interfaces { get; set; }
|
||||
private FontRenderer DejaVu { get; set; }
|
||||
private SpriteBatch SpriteBatch { get; set; }
|
||||
private IPEndPoint EndPoint { get; set; }
|
||||
private ChunkConverter ChunkConverter { get; set; }
|
||||
|
||||
public TrueCraftGame(MultiplayerClient client)
|
||||
public TrueCraftGame(MultiplayerClient client, IPEndPoint endPoint)
|
||||
{
|
||||
Window.Title = "TrueCraft";
|
||||
Content.RootDirectory = "Content";
|
||||
@ -25,13 +28,18 @@ namespace TrueCraft.Client.Linux
|
||||
Graphics.PreferredBackBufferWidth = 1280;
|
||||
Graphics.PreferredBackBufferHeight = 720;
|
||||
Client = client;
|
||||
EndPoint = endPoint;
|
||||
ChunkConverter = new ChunkConverter();
|
||||
Client.ChunkLoaded += (sender, e) => ChunkConverter.QueueChunk(e.Chunk);
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
Interfaces = new List<IGameInterface>();
|
||||
SpriteBatch = new SpriteBatch(GraphicsDevice);
|
||||
base.Initialize();
|
||||
base.Initialize(); // (calls LoadContent)
|
||||
ChunkConverter.Start();
|
||||
Client.Connect(EndPoint);
|
||||
}
|
||||
|
||||
protected override void LoadContent()
|
||||
|
Loading…
x
Reference in New Issue
Block a user