diff --git a/TrueCraft.API/World/IChunk.cs b/TrueCraft.API/World/IChunk.cs index dc23c43..fc1b6b3 100644 --- a/TrueCraft.API/World/IChunk.cs +++ b/TrueCraft.API/World/IChunk.cs @@ -19,6 +19,7 @@ namespace TrueCraft.API.World NibbleArray BlockLight { get; } NibbleArray SkyLight { get; } int GetHeight(byte x, byte z); + void UpdateHeightMap(); byte GetBlockID(Coordinates3D coordinates); byte GetMetadata(Coordinates3D coordinates); byte GetSkyLight(Coordinates3D coordinates); diff --git a/TrueCraft.Client/Handlers/ChunkHandler.cs b/TrueCraft.Client/Handlers/ChunkHandler.cs index 23543ea..44c5b3f 100644 --- a/TrueCraft.Client/Handlers/ChunkHandler.cs +++ b/TrueCraft.Client/Handlers/ChunkHandler.cs @@ -5,6 +5,7 @@ using TrueCraft.API; using TrueCraft.Core.World; using MonoGame.Utilities; using TrueCraft.Client.Events; +using TrueCraft.API.World; namespace TrueCraft.Client.Handlers { @@ -20,15 +21,75 @@ namespace TrueCraft.Client.Handlers 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 + IChunk chunk; + var adjustedCoords = client.World.World.FindBlockPosition( + new Coordinates3D(packet.X, packet.Y, packet.Z), out chunk); + if (packet.Width == Chunk.Width + && packet.Height == Chunk.Height + && packet.Depth == Chunk.Depth) // Fast path + { + // Block IDs + Array.Copy(data, 0, chunk.Blocks, 0, chunk.Blocks.Length); + // Block metadata + Array.Copy(data, chunk.Blocks.Length, chunk.Metadata.Data, 0, chunk.Metadata.Data.Length); + // Block light + Array.Copy(data, chunk.Blocks.Length + chunk.Metadata.Data.Length, + chunk.BlockLight.Data, 0, chunk.BlockLight.Data.Length); + // Sky light + Array.Copy(data, chunk.Blocks.Length + chunk.Metadata.Data.Length + chunk.BlockLight.Data.Length, + chunk.SkyLight.Data, 0, chunk.SkyLight.Data.Length); + } + else // Slow path + { + int x = adjustedCoords.X, y = adjustedCoords.Y, z = adjustedCoords.Z; + int fullLength = packet.Width * packet.Height * packet.Depth; // Length of full sized byte section + int nibbleLength = fullLength / 2; // Length of nibble sections + for (int i = 0; i < fullLength; i++) // Iterate through block IDs + { + chunk.SetBlockID(new Coordinates3D(x, y, z), data[i]); + y++; + if (y >= packet.Height) + { + y = 0; + z++; + if (z >= packet.Depth) + { + z = 0; + x++; + if (x >= packet.Width) + { + x = 0; + } + } + } + } + x = adjustedCoords.X; y = adjustedCoords.Y; z = adjustedCoords.Z; + for (int i = fullLength; i < nibbleLength; i++) // Iterate through metadata + { + byte m = data[i]; + chunk.SetMetadata(new Coordinates3D(x, y, z), (byte)(m & 0xF)); + chunk.SetMetadata(new Coordinates3D(x, y + 1, z), (byte)(m & 0xF0 << 8)); + y += 2; + if (y >= packet.Height) + { + y = 0; + z++; + if (z >= packet.Depth) + { + z = 0; + x++; + if (x >= packet.Width) + { + x = 0; + } + } + } + } + // TODO: Lighting + } + chunk.UpdateHeightMap(); client.OnChunkLoaded(new ChunkEventArgs(new ReadOnlyChunk(chunk))); } } diff --git a/TrueCraft.Client/Handlers/PacketHandlers.cs b/TrueCraft.Client/Handlers/PacketHandlers.cs index 4df5d62..0916302 100644 --- a/TrueCraft.Client/Handlers/PacketHandlers.cs +++ b/TrueCraft.Client/Handlers/PacketHandlers.cs @@ -14,6 +14,7 @@ namespace TrueCraft.Client.Handlers client.RegisterPacketHandler(new HandshakeResponsePacket().ID, HandleHandshake); client.RegisterPacketHandler(new ChatMessagePacket().ID, HandleChatMessage); client.RegisterPacketHandler(new SetPlayerPositionPacket().ID, HandlePositionAndLook); + client.RegisterPacketHandler(new LoginResponsePacket().ID, HandleLoginResponse); client.RegisterPacketHandler(new ChunkPreamblePacket().ID, ChunkHandler.HandleChunkPreamble); client.RegisterPacketHandler(new ChunkDataPacket().ID, ChunkHandler.HandleChunkData); @@ -32,10 +33,18 @@ namespace TrueCraft.Client.Handlers client.QueuePacket(new LoginRequestPacket(PacketReader.Version, "TestUser")); } + public static void HandleLoginResponse(IPacket _packet, MultiplayerClient client) + { + client.QueuePacket(new PlayerGroundedPacket()); + } + public static void HandlePositionAndLook(IPacket _packet, MultiplayerClient client) { var packet = (SetPlayerPositionPacket)_packet; 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 } } diff --git a/TrueCraft.Client/MultiplayerClient.cs b/TrueCraft.Client/MultiplayerClient.cs index 128217e..7fbebcb 100644 --- a/TrueCraft.Client/MultiplayerClient.cs +++ b/TrueCraft.Client/MultiplayerClient.cs @@ -26,6 +26,7 @@ namespace TrueCraft.Client public ReadOnlyWorld World { get; private set; } public PhysicsEngine Physics { get; set; } + public bool LoggedIn { get; internal set; } private TcpClient Client { get; set; } private IMinecraftStream Stream { get; set; } diff --git a/TrueCraft.Client/TrueCraftGame.cs b/TrueCraft.Client/TrueCraftGame.cs index 045ae12..8ddbf04 100644 --- a/TrueCraft.Client/TrueCraftGame.cs +++ b/TrueCraft.Client/TrueCraftGame.cs @@ -10,6 +10,8 @@ using TrueCraft.API; using TrueCraft.Client.Rendering; using System.Linq; using System.ComponentModel; +using TrueCraft.Core.Networking.Packets; +using TrueCraft.API.World; namespace TrueCraft.Client { @@ -29,6 +31,8 @@ namespace TrueCraft.Client private Matrix Camera; private Matrix Perspective; private BoundingFrustum CameraView; + private bool MouseCaptured; + private KeyboardState PreviousKeyboardState; private BasicEffect OpaqueEffect, TransparentEffect; @@ -45,6 +49,7 @@ namespace TrueCraft.Client NextPhysicsUpdate = DateTime.MinValue; ChunkMeshes = new List(); TransparentChunkMeshes = new List(); + MouseCaptured = true; } protected override void Initialize() @@ -68,6 +73,7 @@ namespace TrueCraft.Client var centerY = GraphicsDevice.Viewport.Height / 2; Mouse.SetPosition(centerX, centerY); UpdateMatricies(); + PreviousKeyboardState = Keyboard.GetState(); } void HandleClientPropertyChanged(object sender, PropertyChangedEventArgs e) @@ -118,7 +124,7 @@ namespace TrueCraft.Client base.OnExiting(sender, args); } - protected virtual void UpdateKeyboard(GameTime gameTime, KeyboardState state) + protected virtual void UpdateKeyboard(GameTime gameTime, KeyboardState state, KeyboardState oldState) { if (state.IsKeyDown(Keys.Escape)) Exit(); @@ -133,25 +139,31 @@ namespace TrueCraft.Client delta += Microsoft.Xna.Framework.Vector3.Forward; 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; 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); - var centerX = GraphicsDevice.Viewport.Width / 2; - var centerY = GraphicsDevice.Viewport.Height / 2; - var mouse = Mouse.GetState(); - var look = new Vector2(centerX - mouse.Position.X, centerY - mouse.Position.Y) - * (float)(gameTime.ElapsedGameTime.TotalSeconds * 70); - Mouse.SetPosition(centerX, centerY); - Client.Yaw += look.X; - Client.Pitch += look.Y; - Client.Yaw %= 360; - Client.Pitch = MathHelper.Clamp(Client.Pitch, -90, 90); + if (MouseCaptured) + { + var centerX = GraphicsDevice.Viewport.Width / 2; + var centerY = GraphicsDevice.Viewport.Height / 2; + var mouse = Mouse.GetState(); + var look = new Vector2(centerX - mouse.Position.X, centerY - mouse.Position.Y) + * (float)(gameTime.ElapsedGameTime.TotalSeconds * 70); + Mouse.SetPosition(centerX, centerY); + Client.Yaw += look.X; + Client.Pitch += look.Y; + Client.Yaw %= 360; + Client.Pitch = MathHelper.Clamp(Client.Pitch, -90, 90); - if (look != Vector2.Zero) - UpdateMatricies(); + if (look != Vector2.Zero) + UpdateMatricies(); + } } protected override void Update(GameTime gameTime) @@ -162,11 +174,28 @@ namespace TrueCraft.Client } if (NextPhysicsUpdate < DateTime.Now) { - if (Client.World.FindChunk(new Coordinates2D((int)Client.Position.X, (int)Client.Position.Z)) != null) - Client.Physics.Update(); + 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.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); } - UpdateKeyboard(gameTime, Keyboard.GetState()); + var state = Keyboard.GetState(); + UpdateKeyboard(gameTime, state, PreviousKeyboardState); + PreviousKeyboardState = state; base.Update(gameTime); } @@ -195,7 +224,7 @@ namespace TrueCraft.Client Graphics.GraphicsDevice.Clear(Color.CornflowerBlue); GraphicsDevice.SamplerStates[0] = SamplerState.PointClamp; GraphicsDevice.SamplerStates[1] = SamplerState.PointClamp; - GraphicsDevice.BlendState = BlendState.NonPremultiplied; + GraphicsDevice.BlendState = BlendState.AlphaBlend; OpaqueEffect.View = TransparentEffect.View = Camera; OpaqueEffect.Projection = TransparentEffect.Projection = Perspective; diff --git a/TrueCraft.Core/TrueCraft.Core.csproj b/TrueCraft.Core/TrueCraft.Core.csproj index 95c655b..864d695 100644 --- a/TrueCraft.Core/TrueCraft.Core.csproj +++ b/TrueCraft.Core/TrueCraft.Core.csproj @@ -324,4 +324,14 @@ + + + + + + + + + + \ No newline at end of file diff --git a/TrueCraft.Core/World/Chunk.cs b/TrueCraft.Core/World/Chunk.cs index a01aced..b7d32f2 100644 --- a/TrueCraft.Core/World/Chunk.cs +++ b/TrueCraft.Core/World/Chunk.cs @@ -210,6 +210,28 @@ namespace TrueCraft.Core.World HeightMap[(byte)(x * Width) + z] = value; } + public void UpdateHeightMap() + { + for (byte x = 0; x < Chunk.Width; x++) + { + for (byte z = 0; z < Chunk.Depth; z++) + { + int y; + for (y = Chunk.Height - 1; y >= 0; y--) + { + int index = y + (z * Height) + (x * Height * Width); + if (Blocks[index] != 0) + { + SetHeight(x, z, y); + break; + } + } + if (y == 0) + SetHeight(x, z, 0); + } + } + } + public NbtFile ToNbt() { LastAccessed = DateTime.Now;