mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Add world handling (and fall to ground)
- World is now properly parsed and stored from chunk data - Block changes are also handled and world updated accordingly - Added ground checking, the player will move down to reach the ground - Performance tweaking in Protocol18, using lists instead of arrays - Fix player look not properly skipped causing invalid location after teleport
This commit is contained in:
parent
2e4544fc5a
commit
cb00c28b6e
10 changed files with 661 additions and 91 deletions
99
MinecraftClient/Mapping/Block.cs
Normal file
99
MinecraftClient/Mapping/Block.cs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Minecraft Block
|
||||
/// </summary>
|
||||
public struct Block
|
||||
{
|
||||
/// <summary>
|
||||
/// Storage for block ID and metadata
|
||||
/// </summary>
|
||||
private ushort blockIdAndMeta;
|
||||
|
||||
/// <summary>
|
||||
/// Id of the block
|
||||
/// </summary>
|
||||
public short BlockId
|
||||
{
|
||||
get
|
||||
{
|
||||
return (short)(blockIdAndMeta >> 4);
|
||||
}
|
||||
set
|
||||
{
|
||||
blockIdAndMeta = (ushort)(value << 4 | BlockMeta);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Metadata of the block
|
||||
/// </summary>
|
||||
public byte BlockMeta
|
||||
{
|
||||
get
|
||||
{
|
||||
return (byte)(blockIdAndMeta & 0x0F);
|
||||
}
|
||||
set
|
||||
{
|
||||
blockIdAndMeta = (ushort)((blockIdAndMeta & ~0x0F) | (value & 0x0F));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the block can be passed through or not
|
||||
/// </summary>
|
||||
public bool Solid
|
||||
{
|
||||
get
|
||||
{
|
||||
return BlockId != 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a block of the specified type and metadata
|
||||
/// </summary>
|
||||
/// <param name="type">Block type</param>
|
||||
/// <param name="metadata">Block metadata</param>
|
||||
public Block(short type, byte metadata = 0)
|
||||
{
|
||||
this.blockIdAndMeta = 0;
|
||||
this.BlockId = type;
|
||||
this.BlockMeta = metadata;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a block of the specified type and metadata
|
||||
/// </summary>
|
||||
/// <param name="typeAndMeta"></param>
|
||||
public Block(ushort typeAndMeta)
|
||||
{
|
||||
this.blockIdAndMeta = typeAndMeta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents an empty block
|
||||
/// </summary>
|
||||
public static Block Air
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Block(0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// String representation of the block
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return BlockId.ToString() + (BlockMeta != 0 ? ":" + BlockMeta.ToString() : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
63
MinecraftClient/Mapping/Chunk.cs
Normal file
63
MinecraftClient/Mapping/Chunk.cs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent a chunk of terrain in a Minecraft world
|
||||
/// </summary>
|
||||
public class Chunk
|
||||
{
|
||||
public const int SizeX = 16;
|
||||
public const int SizeY = 16;
|
||||
public const int SizeZ = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Blocks contained into the chunk
|
||||
/// </summary>
|
||||
private readonly Block[,,] blocks = new Block[SizeX, SizeY, SizeZ];
|
||||
|
||||
/// <summary>
|
||||
/// Read, or set the specified block
|
||||
/// </summary>
|
||||
/// <param name="blockX">Block X</param>
|
||||
/// <param name="blockY">Block Y</param>
|
||||
/// <param name="blockZ">Block Z</param>
|
||||
/// <returns>chunk at the given location</returns>
|
||||
public Block this[int blockX, int blockY, int blockZ]
|
||||
{
|
||||
get
|
||||
{
|
||||
if (blockX < 0 || blockX >= SizeX)
|
||||
throw new ArgumentOutOfRangeException("blockX", "Must be between 0 and " + (SizeX - 1) + " (inclusive)");
|
||||
if (blockY < 0 || blockY >= SizeY)
|
||||
throw new ArgumentOutOfRangeException("blockY", "Must be between 0 and " + (SizeY - 1) + " (inclusive)");
|
||||
if (blockZ < 0 || blockZ >= SizeZ)
|
||||
throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (SizeZ - 1) + " (inclusive)");
|
||||
return blocks[blockX, blockY, blockZ];
|
||||
}
|
||||
set
|
||||
{
|
||||
if (blockX < 0 || blockX >= SizeX)
|
||||
throw new ArgumentOutOfRangeException("blockX", "Must be between 0 and " + (SizeX - 1) + " (inclusive)");
|
||||
if (blockY < 0 || blockY >= SizeY)
|
||||
throw new ArgumentOutOfRangeException("blockY", "Must be between 0 and " + (SizeY - 1) + " (inclusive)");
|
||||
if (blockZ < 0 || blockZ >= SizeZ)
|
||||
throw new ArgumentOutOfRangeException("blockZ", "Must be between 0 and " + (SizeZ - 1) + " (inclusive)");
|
||||
blocks[blockX, blockY, blockZ] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get block at the specified location
|
||||
/// </summary>
|
||||
/// <param name="location">Location, a modulo will be applied</param>
|
||||
/// <returns>The block</returns>
|
||||
public Block GetBlock(Location location)
|
||||
{
|
||||
return this[((int)location.X) % Chunk.SizeX, ((int)location.Y) % Chunk.SizeY, ((int)location.Z) % Chunk.SizeZ];
|
||||
}
|
||||
}
|
||||
}
|
||||
48
MinecraftClient/Mapping/ChunkColumn.cs
Normal file
48
MinecraftClient/Mapping/ChunkColumn.cs
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Represent a column of chunks of terrain in a Minecraft world
|
||||
/// </summary>
|
||||
public class ChunkColumn
|
||||
{
|
||||
public const int ColumnSize = 16;
|
||||
|
||||
/// <summary>
|
||||
/// Blocks contained into the chunk
|
||||
/// </summary>
|
||||
private readonly Chunk[] chunks = new Chunk[ColumnSize];
|
||||
|
||||
/// <summary>
|
||||
/// Get or set the specified chunk column
|
||||
/// </summary>
|
||||
/// <param name="chunkX">ChunkColumn X</param>
|
||||
/// <param name="chunkY">ChunkColumn Y</param>
|
||||
/// <returns>chunk at the given location</returns>
|
||||
public Chunk this[int chunkY]
|
||||
{
|
||||
get
|
||||
{
|
||||
return chunks[chunkY];
|
||||
}
|
||||
set
|
||||
{
|
||||
chunks[chunkY] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get chunk at the specified location
|
||||
/// </summary>
|
||||
/// <param name="location">Location, a modulo will be applied</param>
|
||||
/// <returns>The chunk, or null if not loaded</returns>
|
||||
public Chunk GetChunk(Location location)
|
||||
{
|
||||
return this[location.ChunkY];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -46,6 +46,72 @@ namespace MinecraftClient.Mapping
|
|||
Z = z;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The X index of the corresponding chunk in the world
|
||||
/// </summary>
|
||||
public int ChunkX
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((int)X) / Chunk.SizeX;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Y index of the corresponding chunk in the world
|
||||
/// </summary>
|
||||
public int ChunkY
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((int)Y) / Chunk.SizeY;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Z index of the corresponding chunk in the world
|
||||
/// </summary>
|
||||
public int ChunkZ
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((int)Z) / Chunk.SizeY;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The X index of the corresponding block in the corresponding chunk of the world
|
||||
/// </summary>
|
||||
public int ChunkBlockX
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((int)X) % Chunk.SizeX;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Y index of the corresponding block in the corresponding chunk of the world
|
||||
/// </summary>
|
||||
public int ChunkBlockY
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((int)Y) % Chunk.SizeY;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Z index of the corresponding block in the corresponding chunk of the world
|
||||
/// </summary>
|
||||
public int ChunkBlockZ
|
||||
{
|
||||
get
|
||||
{
|
||||
return ((int)Z) % Chunk.SizeZ;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compare two locations. Locations are equals if the integer part of their coordinates are equals.
|
||||
/// </summary>
|
||||
|
|
@ -155,5 +221,14 @@ namespace MinecraftClient.Mapping
|
|||
| (((int)Y) & ~((~0) << 13)) << 13
|
||||
| (((int)Z) & ~((~0) << 06)) << 00;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert the location into a string representation
|
||||
/// </summary>
|
||||
/// <returns>String representation of the location</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("X:{0} Y:{1} Z:{2}", X, Y, Z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
103
MinecraftClient/Mapping/World.cs
Normal file
103
MinecraftClient/Mapping/World.cs
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace MinecraftClient.Mapping
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a Minecraft World
|
||||
/// </summary>
|
||||
public class World
|
||||
{
|
||||
/// <summary>
|
||||
/// The chunks contained into the Minecraft world
|
||||
/// </summary>
|
||||
private Dictionary<int, Dictionary<int, ChunkColumn>> chunks = new Dictionary<int, Dictionary<int, ChunkColumn>>();
|
||||
|
||||
/// <summary>
|
||||
/// Read, set or unload the specified chunk column
|
||||
/// </summary>
|
||||
/// <param name="chunkX">ChunkColumn X</param>
|
||||
/// <param name="chunkY">ChunkColumn Y</param>
|
||||
/// <returns>chunk at the given location</returns>
|
||||
public ChunkColumn this[int chunkX, int chunkZ]
|
||||
{
|
||||
get
|
||||
{
|
||||
//Read a chunk
|
||||
if (chunks.ContainsKey(chunkX))
|
||||
if (chunks[chunkX].ContainsKey(chunkZ))
|
||||
return chunks[chunkX][chunkZ];
|
||||
return null;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
//Update a chunk column
|
||||
if (!chunks.ContainsKey(chunkX))
|
||||
chunks[chunkX] = new Dictionary<int, ChunkColumn>();
|
||||
chunks[chunkX][chunkZ] = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Unload a chunk column
|
||||
if (chunks.ContainsKey(chunkX))
|
||||
{
|
||||
if (chunks[chunkX].ContainsKey(chunkZ))
|
||||
{
|
||||
chunks[chunkX].Remove(chunkZ);
|
||||
if (chunks[chunkX].Count == 0)
|
||||
chunks.Remove(chunkX);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get chunk column at the specified location
|
||||
/// </summary>
|
||||
/// <param name="location">Location to retrieve chunk column</param>
|
||||
/// <returns>The chunk column</returns>
|
||||
public ChunkColumn GetChunkColumn(Location location)
|
||||
{
|
||||
return this[location.ChunkX, location.ChunkZ];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get block at the specified location
|
||||
/// </summary>
|
||||
/// <param name="location">Location to retrieve block from</param>
|
||||
/// <returns>Block at specified location or Air if the location is not loaded</returns>
|
||||
public Block GetBlock(Location location)
|
||||
{
|
||||
ChunkColumn column = GetChunkColumn(location);
|
||||
if (column != null)
|
||||
{
|
||||
Chunk chunk = column.GetChunk(location);
|
||||
if (chunk != null)
|
||||
return chunk.GetBlock(location);
|
||||
}
|
||||
return Block.Air;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set block at the specified location
|
||||
/// </summary>
|
||||
/// <param name="location">Location to set block to</param>
|
||||
/// <param name="block">Block to set</param>
|
||||
public void SetBlock(Location location, Block block)
|
||||
{
|
||||
ChunkColumn column = this[location.ChunkX, location.ChunkZ];
|
||||
if (column != null)
|
||||
{
|
||||
Chunk chunk = column[location.ChunkY];
|
||||
if (chunk == null)
|
||||
column[location.ChunkY] = chunk = new Chunk();
|
||||
chunk[location.ChunkBlockX, location.ChunkBlockY, location.ChunkBlockZ] = block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue