Improve use of concurrent .NET patterns

This commit is contained in:
Drew DeVault 2015-05-17 16:52:16 -06:00
parent bd132b0d6f
commit b308252b88
7 changed files with 159 additions and 74 deletions

View File

@ -99,15 +99,19 @@ namespace TrueCraft.Client
Coordinates3D.West
};
private readonly List<VertexPositionNormalTexture> OpaqueVerticies = new List<VertexPositionNormalTexture>();
private readonly List<int> OpaqueIndicies = new List<int>();
private readonly List<VertexPositionNormalTexture> TransparentVerticies = new List<VertexPositionNormalTexture>();
private readonly List<int> TransparentIndicies = new List<int>();
private readonly HashSet<Coordinates3D> DrawableCoordinates = new HashSet<Coordinates3D>();
private Tuple<Mesh, Mesh> ProcessChunk(ReadOnlyChunk chunk)
{
var opaqueVerticies = new List<VertexPositionNormalTexture>();
var opaqueIndicies = new List<int>();
var transparentVerticies = new List<VertexPositionNormalTexture>();
var transparentIndicies = new List<int>();
var drawableCoordinates = new HashSet<Coordinates3D>();
OpaqueVerticies.Clear();
OpaqueIndicies.Clear();
TransparentVerticies.Clear();
TransparentIndicies.Clear();
DrawableCoordinates.Clear();
var boundingBox = new Microsoft.Xna.Framework.BoundingBox(
new Vector3(chunk.X * Chunk.Width, 0, chunk.Z * Chunk.Depth),
@ -123,7 +127,7 @@ namespace TrueCraft.Client
var id = chunk.GetBlockId(coords);
var provider = BlockRepository.GetBlockProvider(id);
if (id != 0)
drawableCoordinates.Add(coords);
DrawableCoordinates.Add(coords);
if (!provider.Opaque)
{
// Add adjacent blocks
@ -137,14 +141,17 @@ namespace TrueCraft.Client
continue;
}
if (chunk.GetBlockId(next) != 0)
drawableCoordinates.Add(next);
DrawableCoordinates.Add(next);
}
}
}
}
}
foreach (var coords in drawableCoordinates)
var enumerator = DrawableCoordinates.GetEnumerator();
for (int j = 0; j < DrawableCoordinates.Count; j++)
{
var coords = enumerator.Current;
enumerator.MoveNext();
var descriptor = new BlockDescriptor
{
ID = chunk.GetBlockId(coords),
@ -159,23 +166,23 @@ namespace TrueCraft.Client
int[] i;
var v = BlockRenderer.RenderBlock(provider, descriptor,
new Vector3(chunk.X * Chunk.Width + coords.X, coords.Y, chunk.Z * Chunk.Depth + coords.Z),
opaqueVerticies.Count, out i);
opaqueVerticies.AddRange(v);
opaqueIndicies.AddRange(i);
OpaqueVerticies.Count, out i);
OpaqueVerticies.AddRange(v);
OpaqueIndicies.AddRange(i);
}
else
{
int[] i;
var v = BlockRenderer.RenderBlock(provider, descriptor,
new Vector3(chunk.X * Chunk.Width + coords.X, coords.Y, chunk.Z * Chunk.Depth + coords.Z),
transparentVerticies.Count, out i);
transparentVerticies.AddRange(v);
transparentIndicies.AddRange(i);
TransparentVerticies.Count, out i);
TransparentVerticies.AddRange(v);
TransparentIndicies.AddRange(i);
}
}
var meshes = new Tuple<Mesh, Mesh>(
new Mesh(Graphics, opaqueVerticies.ToArray(), opaqueIndicies.ToArray(), false),
new Mesh(Graphics, transparentVerticies.ToArray(), transparentIndicies.ToArray(), false));
new Mesh(Graphics, OpaqueVerticies.ToArray(), OpaqueIndicies.ToArray(), false),
new Mesh(Graphics, TransparentVerticies.ToArray(), TransparentIndicies.ToArray(), false));
meshes.Item1.BoundingBox = boundingBox;
meshes.Item2.BoundingBox = boundingBox;
return meshes;

View File

@ -4,6 +4,7 @@ using TrueCraft.Core.Networking.Packets;
using TrueCraft.Core.Networking;
using TrueCraft.Client.Events;
using TrueCraft.API;
using System.Diagnostics;
namespace TrueCraft.Client.Handlers
{
@ -29,7 +30,12 @@ namespace TrueCraft.Client.Handlers
public static void HandleHandshake(IPacket _packet, MultiplayerClient client)
{
var packet = (HandshakeResponsePacket)_packet;
Console.WriteLine("Got handshake with {0}", packet.ConnectionHash); // TODO: Authenticate?
if (packet.ConnectionHash != "-")
{
Console.WriteLine("Online mode is not supported");
Process.GetCurrentProcess().Kill();
}
// TODO: Authentication
client.QueuePacket(new LoginRequestPacket(PacketReader.Version, "TestUser"));
}
@ -44,7 +50,6 @@ namespace TrueCraft.Client.Handlers
client._Position = new Vector3(packet.X, packet.Y, packet.Z);
client.QueuePacket(packet);
client.LoggedIn = true;
Console.WriteLine("Got P+L: {0}", client._Position);
// TODO: Pitch and yaw
}
}

View File

@ -16,7 +16,7 @@ namespace TrueCraft.Client.Rendering.Blocks
}
private static Vector2 TextureMap = new Vector2(0, 0);
private static Vector2 BottomTexture = new Vector2(2, 0);
private static Vector2 EndsTexture = new Vector2(2, 0);
private static Vector2 SideTexture = new Vector2(3, 0);
private static Vector2[] Texture =
{
@ -46,10 +46,10 @@ namespace TrueCraft.Client.Rendering.Blocks
TextureMap,
TextureMap + Vector2.UnitX,
// Negative Y
BottomTexture + Vector2.UnitX + Vector2.UnitY,
BottomTexture + Vector2.UnitY,
BottomTexture,
BottomTexture + Vector2.UnitX,
EndsTexture + Vector2.UnitX + Vector2.UnitY,
EndsTexture + Vector2.UnitY,
EndsTexture,
EndsTexture + Vector2.UnitX,
};
public override VertexPositionNormalTexture[] Render(BlockDescriptor descriptor, Vector3 offset,

View File

@ -0,0 +1,60 @@
using System;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using TrueCraft.Core.Logic.Blocks;
using TrueCraft.API.Logic;
namespace TrueCraft.Client.Rendering.Blocks
{
public class LogRenderer : BlockRenderer
{
static LogRenderer()
{
BlockRenderer.RegisterRenderer(WoodBlock.BlockID, new LogRenderer());
for (int i = 0; i < Texture.Length; i++)
Texture[i] *= new Vector2(16f / 256f);
}
private static Vector2 EndsTexture = new Vector2(5, 1);
private static Vector2 SideTexture = new Vector2(4, 1);
private static Vector2[] Texture =
{
// Positive Z
SideTexture + Vector2.UnitX + Vector2.UnitY,
SideTexture + Vector2.UnitY,
SideTexture,
SideTexture + Vector2.UnitX,
// Negative Z
SideTexture + Vector2.UnitX + Vector2.UnitY,
SideTexture + Vector2.UnitY,
SideTexture,
SideTexture + Vector2.UnitX,
// Positive X
SideTexture + Vector2.UnitX + Vector2.UnitY,
SideTexture + Vector2.UnitY,
SideTexture,
SideTexture + Vector2.UnitX,
// Negative X
SideTexture + Vector2.UnitX + Vector2.UnitY,
SideTexture + Vector2.UnitY,
SideTexture,
SideTexture + Vector2.UnitX,
// Positive Y
EndsTexture + Vector2.UnitX + Vector2.UnitY,
EndsTexture + Vector2.UnitY,
EndsTexture,
EndsTexture + Vector2.UnitX,
// Negative Y
EndsTexture + Vector2.UnitX + Vector2.UnitY,
EndsTexture + Vector2.UnitY,
EndsTexture,
EndsTexture + Vector2.UnitX,
};
public override VertexPositionNormalTexture[] Render(BlockDescriptor descriptor, Vector3 offset,
Tuple<int, int> textureMap, int indiciesOffset, out int[] indicies)
{
return CreateUniformCube(offset, Texture, indiciesOffset, out indicies);
}
}
}

View File

@ -15,7 +15,7 @@ namespace TrueCraft.Client.Rendering
public Mesh(GraphicsDevice device, VertexPositionNormalTexture[] verticies, int[] indicies, bool calculateBounds = true)
{
Empty = verticies.Length == 0 || indicies.Length == 0;
Empty = verticies.Length == 0 && indicies.Length == 0;
if (!Empty)
{
Verticies = new VertexBuffer(device, VertexPositionNormalTexture.VertexDeclaration,

View File

@ -87,6 +87,7 @@
<Compile Include="Rendering\Blocks\TNTRenderer.cs" />
<Compile Include="Rendering\Blocks\SnowRenderer.cs" />
<Compile Include="Rendering\Blocks\TorchRenderer.cs" />
<Compile Include="Rendering\Blocks\LogRenderer.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>

View File

@ -12,6 +12,7 @@ using System.Linq;
using System.ComponentModel;
using TrueCraft.Core.Networking.Packets;
using TrueCraft.API.World;
using System.Collections.Concurrent;
namespace TrueCraft.Client
{
@ -26,8 +27,10 @@ namespace TrueCraft.Client
private ChunkConverter ChunkConverter { get; set; }
private DateTime NextPhysicsUpdate { get; set; }
private List<Mesh> ChunkMeshes { get; set; }
private ConcurrentBag<Action> PendingMainThreadActions { get; set; }
private ConcurrentBag<Mesh> IncomingChunks { get; set; }
private ConcurrentBag<Mesh> IncomingTransparentChunks { get; set; }
private List<Mesh> TransparentChunkMeshes { get; set; }
private readonly object ChunkMeshesLock = new object();
private Matrix Camera;
private Matrix Perspective;
private BoundingFrustum CameraView;
@ -49,6 +52,9 @@ namespace TrueCraft.Client
NextPhysicsUpdate = DateTime.MinValue;
ChunkMeshes = new List<Mesh>();
TransparentChunkMeshes = new List<Mesh>();
IncomingChunks = new ConcurrentBag<Mesh>();
IncomingTransparentChunks = new ConcurrentBag<Mesh>();
PendingMainThreadActions = new ConcurrentBag<Action>();
MouseCaptured = true;
}
@ -61,11 +67,8 @@ namespace TrueCraft.Client
Client.ChunkLoaded += (sender, e) => ChunkConverter.QueueChunk(e.Chunk);
ChunkConverter.Start((opaque, transparent) =>
{
lock (ChunkMeshesLock)
{
ChunkMeshes.Add(opaque);
TransparentChunkMeshes.Add(transparent);
}
IncomingChunks.Add(opaque);
IncomingTransparentChunks.Add(transparent);
});
Client.PropertyChanged += HandleClientPropertyChanged;
Client.Connect(EndPoint);
@ -84,8 +87,7 @@ namespace TrueCraft.Client
UpdateMatricies();
var sorter = new ChunkConverter.ChunkSorter(new Coordinates3D(
(int)Client.Position.X, 0, (int)Client.Position.Z));
lock (ChunkMeshesLock)
TransparentChunkMeshes.Sort(sorter);
PendingMainThreadActions.Add(() => TransparentChunkMeshes.Sort(sorter));
break;
}
}
@ -128,9 +130,12 @@ namespace TrueCraft.Client
{
if (state.IsKeyDown(Keys.Escape))
Exit();
// TODO: Rebindable keys
// TODO: Horizontal terrain collisions
Microsoft.Xna.Framework.Vector3 delta = Microsoft.Xna.Framework.Vector3.Zero;
if (state.IsKeyDown(Keys.Left) || state.IsKeyDown(Keys.A))
delta += Microsoft.Xna.Framework.Vector3.Left;
if (state.IsKeyDown(Keys.Right) || state.IsKeyDown(Keys.D))
@ -140,14 +145,16 @@ namespace TrueCraft.Client
if (state.IsKeyDown(Keys.Down) || state.IsKeyDown(Keys.S))
delta += Microsoft.Xna.Framework.Vector3.Backward;
if (state.IsKeyUp(Keys.Tab) && oldState.IsKeyDown(Keys.Tab))
MouseCaptured = !MouseCaptured;
if (delta != Microsoft.Xna.Framework.Vector3.Zero)
{
var lookAt = Microsoft.Xna.Framework.Vector3.Transform(
delta, Matrix.CreateRotationY(MathHelper.ToRadians(Client.Yaw)));
Client.Position += new TrueCraft.API.Vector3(lookAt.X, lookAt.Y, lookAt.Z) * (gameTime.ElapsedGameTime.TotalSeconds * 4.3717);
}
if (state.IsKeyUp(Keys.Tab) && oldState.IsKeyDown(Keys.Tab))
MouseCaptured = !MouseCaptured;
if (MouseCaptured)
{
var centerX = GraphicsDevice.Viewport.Width / 2;
@ -172,25 +179,31 @@ namespace TrueCraft.Client
{
i.Update(gameTime);
}
if (NextPhysicsUpdate < DateTime.Now)
Mesh mesh;
if (IncomingChunks.TryTake(out mesh))
ChunkMeshes.Add(mesh);
if (IncomingTransparentChunks.TryTake(out mesh))
TransparentChunkMeshes.Add(mesh);
Action action;
if (PendingMainThreadActions.TryTake(out action))
action();
if (NextPhysicsUpdate < DateTime.Now && Client.LoggedIn)
{
IChunk chunk;
var adjusted = Client.World.World.FindBlockPosition(new Coordinates3D((int)Client.Position.X, 0, (int)Client.Position.Z), out chunk);
if (Client.LoggedIn && chunk != null)
if (chunk != null)
{
if (chunk.GetHeight((byte)adjusted.X, (byte)adjusted.Z) != 0)
Client.Physics.Update();
}
if (Client.LoggedIn)
{
// NOTE: This is to make the vanilla server send us chunk packets
// We should eventually make some means of detecing that we're on a vanilla server to enable this
// It's a waste of bandwidth to do it on a TrueCraft server
Console.WriteLine("Sending position packet");
Client.QueuePacket(new PlayerGroundedPacket { OnGround = true });
Client.QueuePacket(new PlayerPositionAndLookPacket(Client.Position.X, Client.Position.Y,
Client.Position.Y + MultiplayerClient.Height, Client.Position.Z, Client.Yaw, Client.Pitch, false));
}
NextPhysicsUpdate = DateTime.Now.AddMilliseconds(1000 / 20);
}
var state = Keyboard.GetState();
@ -217,6 +230,10 @@ namespace TrueCraft.Client
Perspective = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(70f), GraphicsDevice.Viewport.AspectRatio, 0.01f, 1000f);
CameraView = new BoundingFrustum(Camera * Perspective);
OpaqueEffect.View = TransparentEffect.View = Camera;
OpaqueEffect.Projection = TransparentEffect.Projection = Perspective;
OpaqueEffect.World = TransparentEffect.World = Matrix.Identity;
}
protected override void Draw(GameTime gameTime)
@ -226,45 +243,40 @@ namespace TrueCraft.Client
GraphicsDevice.SamplerStates[1] = SamplerState.PointClamp;
GraphicsDevice.BlendState = BlendState.AlphaBlend;
OpaqueEffect.View = TransparentEffect.View = Camera;
OpaqueEffect.Projection = TransparentEffect.Projection = Perspective;
OpaqueEffect.World = TransparentEffect.World = Matrix.Identity;
int verticies = 0, chunks = 0;
lock (ChunkMeshesLock)
{
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
foreach (var chunk in ChunkMeshes)
for (int i = 0; i < ChunkMeshes.Count; i++)
{
if (CameraView.Intersects(chunk.BoundingBox))
if (CameraView.Intersects(ChunkMeshes[i].BoundingBox))
{
verticies += chunk.Verticies.VertexCount;
verticies += ChunkMeshes[i].Verticies.VertexCount;
chunks++;
chunk.Draw(OpaqueEffect);
ChunkMeshes[i].Draw(OpaqueEffect);
}
}
GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
foreach (var chunk in TransparentChunkMeshes)
for (int i = 0; i < TransparentChunkMeshes.Count; i++)
{
if (CameraView.Intersects(chunk.BoundingBox))
if (CameraView.Intersects(TransparentChunkMeshes[i].BoundingBox))
{
if (chunk.Verticies != null)
verticies += chunk.Verticies.VertexCount;
chunk.Draw(TransparentEffect);
}
if (TransparentChunkMeshes[i].Verticies != null)
verticies += TransparentChunkMeshes[i].Verticies.VertexCount;
TransparentChunkMeshes[i].Draw(TransparentEffect);
}
}
GraphicsDevice.DepthStencilState = DepthStencilState.Default;
SpriteBatch.Begin();
foreach (var i in Interfaces)
for (int i = 0; i < Interfaces.Count; i++)
{
i.DrawSprites(gameTime, SpriteBatch);
Interfaces[i].DrawSprites(gameTime, SpriteBatch);
}
int fps = (int)(1 / gameTime.ElapsedGameTime.TotalSeconds);
DejaVu.DrawText(SpriteBatch, 0, GraphicsDevice.Viewport.Height - 30,
string.Format("{0} FPS, {1} verticies, {2} chunks", fps + 1, verticies, chunks));
SpriteBatch.End();
base.Draw(gameTime);
}
}