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

View File

@ -4,6 +4,7 @@ using TrueCraft.Core.Networking.Packets;
using TrueCraft.Core.Networking; using TrueCraft.Core.Networking;
using TrueCraft.Client.Events; using TrueCraft.Client.Events;
using TrueCraft.API; using TrueCraft.API;
using System.Diagnostics;
namespace TrueCraft.Client.Handlers namespace TrueCraft.Client.Handlers
{ {
@ -29,7 +30,12 @@ namespace TrueCraft.Client.Handlers
public static void HandleHandshake(IPacket _packet, MultiplayerClient client) public static void HandleHandshake(IPacket _packet, MultiplayerClient client)
{ {
var packet = (HandshakeResponsePacket)_packet; 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")); 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._Position = new Vector3(packet.X, packet.Y, packet.Z);
client.QueuePacket(packet); client.QueuePacket(packet);
client.LoggedIn = true; client.LoggedIn = true;
Console.WriteLine("Got P+L: {0}", client._Position);
// TODO: Pitch and yaw // 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 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 SideTexture = new Vector2(3, 0);
private static Vector2[] Texture = private static Vector2[] Texture =
{ {
@ -46,10 +46,10 @@ namespace TrueCraft.Client.Rendering.Blocks
TextureMap, TextureMap,
TextureMap + Vector2.UnitX, TextureMap + Vector2.UnitX,
// Negative Y // Negative Y
BottomTexture + Vector2.UnitX + Vector2.UnitY, EndsTexture + Vector2.UnitX + Vector2.UnitY,
BottomTexture + Vector2.UnitY, EndsTexture + Vector2.UnitY,
BottomTexture, EndsTexture,
BottomTexture + Vector2.UnitX, EndsTexture + Vector2.UnitX,
}; };
public override VertexPositionNormalTexture[] Render(BlockDescriptor descriptor, Vector3 offset, 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) 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) if (!Empty)
{ {
Verticies = new VertexBuffer(device, VertexPositionNormalTexture.VertexDeclaration, Verticies = new VertexBuffer(device, VertexPositionNormalTexture.VertexDeclaration,

View File

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

View File

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