forked from mirrors/NBTExplorer
Forking Substrate to separate source tree
This commit is contained in:
parent
496cd18b07
commit
99e986b338
31 changed files with 6828 additions and 0 deletions
132
Substrate/SubstrateCS/Source/Block.cs
Normal file
132
Substrate/SubstrateCS/Source/Block.cs
Normal file
|
@ -0,0 +1,132 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
using NBT;
|
||||
using Utility;
|
||||
|
||||
public class Block : IBlock, ICopyable<Block>
|
||||
{
|
||||
private int _id;
|
||||
private int _data;
|
||||
private int _skylight;
|
||||
private int _blocklight;
|
||||
|
||||
private TileEntity _tileEntity;
|
||||
|
||||
public BlockInfo Info
|
||||
{
|
||||
get { return BlockInfo.BlockTable[_id]; }
|
||||
}
|
||||
|
||||
public int ID
|
||||
{
|
||||
get { return _id; }
|
||||
set
|
||||
{
|
||||
BlockInfoEx info1 = BlockInfo.BlockTable[_id] as BlockInfoEx;
|
||||
BlockInfoEx info2 = BlockInfo.BlockTable[value] as BlockInfoEx;
|
||||
|
||||
if (info1 != info2) {
|
||||
if (info1 != null) {
|
||||
_tileEntity = null;
|
||||
}
|
||||
|
||||
if (info2 != null) {
|
||||
_tileEntity = TileEntityFactory.Create(info2.TileEntityName);
|
||||
}
|
||||
}
|
||||
|
||||
_id = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int Data
|
||||
{
|
||||
get { return _data; }
|
||||
set
|
||||
{
|
||||
if (BlockManager.EnforceDataLimits && BlockInfo.BlockTable[_id] != null) {
|
||||
if (!BlockInfo.BlockTable[_id].TestData(value)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
_data = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int SkyLight
|
||||
{
|
||||
get { return _skylight; }
|
||||
set { _skylight = value; }
|
||||
}
|
||||
|
||||
public int BlockLight
|
||||
{
|
||||
get { return _blocklight; }
|
||||
set { _blocklight = value; }
|
||||
}
|
||||
|
||||
public Block (int id)
|
||||
{
|
||||
_id = id;
|
||||
}
|
||||
|
||||
public Block (int id, int data)
|
||||
{
|
||||
_id = id;
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public Block (IChunk chunk, int lx, int ly, int lz)
|
||||
{
|
||||
_id = chunk.GetBlockID(lx, ly, lz);
|
||||
_data = chunk.GetBlockData(lx, ly, lz);
|
||||
_skylight = chunk.GetBlockSkyLight(lx, ly, lz);
|
||||
_blocklight = chunk.GetBlockLight(lx, ly, lz);
|
||||
_tileEntity = chunk.GetTileEntity(lx, ly, lz).Copy();
|
||||
}
|
||||
|
||||
public TileEntity GetTileEntity ()
|
||||
{
|
||||
return _tileEntity;
|
||||
}
|
||||
|
||||
public bool SetTileEntity (TileEntity te)
|
||||
{
|
||||
BlockInfoEx info = BlockInfo.BlockTable[_id] as BlockInfoEx;
|
||||
if (info == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (te.GetType() != TileEntityFactory.Lookup(info.TileEntityName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_tileEntity = te;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ClearTileEntity ()
|
||||
{
|
||||
_tileEntity = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
#region ICopyable<Block> Members
|
||||
|
||||
public Block Copy ()
|
||||
{
|
||||
Block block = new Block(_id, _data);
|
||||
block._blocklight = _blocklight;
|
||||
block._skylight = _skylight;
|
||||
block._tileEntity = _tileEntity.Copy();
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
499
Substrate/SubstrateCS/Source/BlockInfo.cs
Normal file
499
Substrate/SubstrateCS/Source/BlockInfo.cs
Normal file
|
@ -0,0 +1,499 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
using NBT;
|
||||
|
||||
public enum BlockType
|
||||
{
|
||||
AIR = 0,
|
||||
STONE = 1,
|
||||
GRASS = 2,
|
||||
DIRT = 3,
|
||||
COBBLESTONE = 4,
|
||||
WOOD_PLANK = 5,
|
||||
SAPLING = 6,
|
||||
BEDROCK = 7,
|
||||
WATER = 8,
|
||||
STATIONARY_WATER = 9,
|
||||
LAVA = 10,
|
||||
STATIONARY_LAVA = 11,
|
||||
SAND = 12,
|
||||
GRAVEL = 13,
|
||||
GOLD_ORE = 14,
|
||||
IRON_ORE = 15,
|
||||
COAL_ORE = 16,
|
||||
WOOD = 17,
|
||||
LEAVES = 18,
|
||||
SPONGE = 19,
|
||||
GLASS = 20,
|
||||
LAPIS_ORE = 21,
|
||||
LAPIS_BLOCK = 22,
|
||||
DISPENSER = 23,
|
||||
SANDSTONE = 24,
|
||||
NOTE_BLOCK = 25,
|
||||
BED = 26,
|
||||
WOOL = 35,
|
||||
YELLOW_FLOWER = 37,
|
||||
RED_ROSE = 38,
|
||||
BROWN_MUSHROOM = 39,
|
||||
RED_MUSHROOM = 40,
|
||||
GOLD_BLOCK = 41,
|
||||
IRON_BLOCK = 42,
|
||||
DOUBLE_SLAB = 43,
|
||||
SLAB = 44,
|
||||
BRICK_BLOCK = 45,
|
||||
TNT = 46,
|
||||
BOOKSHELF = 47,
|
||||
MOSS_STONE = 48,
|
||||
OBSIDIAN = 49,
|
||||
TORCH = 50,
|
||||
FIRE = 51,
|
||||
MONSTER_SPAWNER = 52,
|
||||
WOOD_STAIRS = 53,
|
||||
CHEST = 54,
|
||||
REDSTONE_WIRE = 55,
|
||||
DIAMOND_ORE = 56,
|
||||
DIAMOND_BLOCK = 57,
|
||||
CRAFTING_TABLE = 58,
|
||||
CROPS = 59,
|
||||
FARMLAND = 60,
|
||||
FURNACE = 61,
|
||||
BURNING_FURNACE = 62,
|
||||
SIGN_POST = 63,
|
||||
WOOD_DOOR = 64,
|
||||
LADDER = 65,
|
||||
RAILS = 66,
|
||||
COBBLESTONE_STAIRS = 67,
|
||||
WALL_SIGN = 68,
|
||||
LEVER = 69,
|
||||
STONE_PLATE = 70,
|
||||
IRON_DOOR = 71,
|
||||
WOOD_PLATE = 72,
|
||||
REDSTONE_ORE = 73,
|
||||
GLOWING_REDSTONE_ORE = 74,
|
||||
REDSTONE_TORCH_OFF = 75,
|
||||
REDSTONE_TORCH_ON = 76,
|
||||
STONE_BUTTON = 77,
|
||||
SNOW = 78,
|
||||
ICE = 79,
|
||||
SNOW_BLOCK = 80,
|
||||
CACTUS = 81,
|
||||
CLAY_BLOCK = 82,
|
||||
SUGAR_CANE = 83,
|
||||
JUKEBOX = 84,
|
||||
FENCE = 85,
|
||||
PUMPKIN = 86,
|
||||
NETHERRACK = 87,
|
||||
SOUL_SAND = 88,
|
||||
GLOWSTONE_BLOCK = 89,
|
||||
PORTAL = 90,
|
||||
JACK_O_LANTERN = 91,
|
||||
CAKE_BLOCK = 92,
|
||||
REDSTONE_REPEATER_ON = 93,
|
||||
REDSTONE_REPEATER_OFF = 94,
|
||||
LOCKED_CHEST = 95,
|
||||
}
|
||||
|
||||
public class BlockInfo
|
||||
{
|
||||
public const int MAX_BLOCKS = 256;
|
||||
|
||||
public const int MAX_OPACITY = 15;
|
||||
public const int MIN_OPACITY = 0;
|
||||
public const int MAX_LUMINANCE = 15;
|
||||
public const int MIN_LUMINANCE = 0;
|
||||
|
||||
private static BlockInfo[] _blockTable;
|
||||
private static int[] _opacityTable;
|
||||
private static int[] _luminanceTable;
|
||||
|
||||
public class ItemCache<T>
|
||||
{
|
||||
private T[] _cache;
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get { return _cache[index]; }
|
||||
}
|
||||
|
||||
public ItemCache (T[] cache)
|
||||
{
|
||||
_cache = cache;
|
||||
}
|
||||
}
|
||||
|
||||
private class DataLimits
|
||||
{
|
||||
private int _low;
|
||||
private int _high;
|
||||
private int _bitmask;
|
||||
|
||||
public int Low
|
||||
{
|
||||
get { return _low; }
|
||||
}
|
||||
|
||||
public int High
|
||||
{
|
||||
get { return _high; }
|
||||
}
|
||||
|
||||
public int Bitmask
|
||||
{
|
||||
get { return _bitmask; }
|
||||
}
|
||||
|
||||
public DataLimits (int low, int high, int bitmask)
|
||||
{
|
||||
_low = low;
|
||||
_high = high;
|
||||
_bitmask = bitmask;
|
||||
}
|
||||
|
||||
public bool Test (int data)
|
||||
{
|
||||
int rdata = data & ~_bitmask;
|
||||
return rdata >= _low && rdata <= _high;
|
||||
}
|
||||
}
|
||||
|
||||
private int _id = 0;
|
||||
private string _name = "";
|
||||
private int _opacity = MAX_OPACITY;
|
||||
private int _luminance = MIN_LUMINANCE;
|
||||
|
||||
private DataLimits _dataLimits;
|
||||
|
||||
public static ItemCache<BlockInfo> BlockTable;
|
||||
|
||||
public static ItemCache<int> OpacityTable;
|
||||
|
||||
public static ItemCache<int> LuminanceTable;
|
||||
|
||||
public static ItemCache<NBTCompoundNode> SchemaTable;
|
||||
|
||||
public int ID
|
||||
{
|
||||
get { return _id; }
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
}
|
||||
|
||||
public int Opacity
|
||||
{
|
||||
get { return _opacity; }
|
||||
}
|
||||
|
||||
public int Luminance
|
||||
{
|
||||
get { return _luminance; }
|
||||
}
|
||||
|
||||
public BlockInfo (int id)
|
||||
{
|
||||
_id = id;
|
||||
_blockTable[_id] = this;
|
||||
}
|
||||
|
||||
public BlockInfo (int id, string name)
|
||||
{
|
||||
_id = id;
|
||||
_name = name;
|
||||
_blockTable[_id] = this;
|
||||
}
|
||||
|
||||
public BlockInfo SetOpacity (int opacity)
|
||||
{
|
||||
_opacity = MIN_OPACITY + opacity;
|
||||
_opacityTable[_id] = _opacity;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockInfo SetLuminance (int luminance)
|
||||
{
|
||||
_luminance = luminance;
|
||||
_luminanceTable[_id] = _luminance;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlockInfo SetDataLimits (int low, int high, int bitmask)
|
||||
{
|
||||
_dataLimits = new DataLimits(low, high, bitmask);
|
||||
return this;
|
||||
}
|
||||
|
||||
public bool TestData (int data)
|
||||
{
|
||||
if (_dataLimits == null) {
|
||||
return true;
|
||||
}
|
||||
return _dataLimits.Test(data);
|
||||
}
|
||||
|
||||
public static BlockInfo Air;
|
||||
public static BlockInfo Stone;
|
||||
public static BlockInfo Grass;
|
||||
public static BlockInfo Dirt;
|
||||
public static BlockInfo Cobblestone;
|
||||
public static BlockInfo WoodPlank;
|
||||
public static BlockInfo Sapling;
|
||||
public static BlockInfo Bedrock;
|
||||
public static BlockInfo Water;
|
||||
public static BlockInfo StationaryWater;
|
||||
public static BlockInfo Lava;
|
||||
public static BlockInfo StationaryLava;
|
||||
public static BlockInfo Sand;
|
||||
public static BlockInfo Gravel;
|
||||
public static BlockInfo GoldOre;
|
||||
public static BlockInfo IronOre;
|
||||
public static BlockInfo CoalOre;
|
||||
public static BlockInfo Wood;
|
||||
public static BlockInfo Leaves;
|
||||
public static BlockInfo Sponge;
|
||||
public static BlockInfo Glass;
|
||||
public static BlockInfo LapisOre;
|
||||
public static BlockInfo LapisBlock;
|
||||
public static BlockInfoEx Dispenser;
|
||||
public static BlockInfo Sandstone;
|
||||
public static BlockInfoEx NoteBlock;
|
||||
public static BlockInfo Bed;
|
||||
public static BlockInfo Wool;
|
||||
public static BlockInfo YellowFlower;
|
||||
public static BlockInfo RedRose;
|
||||
public static BlockInfo BrownMushroom;
|
||||
public static BlockInfo RedMushroom;
|
||||
public static BlockInfo GoldBlock;
|
||||
public static BlockInfo IronBlock;
|
||||
public static BlockInfo DoubleSlab;
|
||||
public static BlockInfo Slab;
|
||||
public static BlockInfo BrickBlock;
|
||||
public static BlockInfo TNT;
|
||||
public static BlockInfo Bookshelf;
|
||||
public static BlockInfo MossStone;
|
||||
public static BlockInfo Obsidian;
|
||||
public static BlockInfo Torch;
|
||||
public static BlockInfo Fire;
|
||||
public static BlockInfoEx MonsterSpawner;
|
||||
public static BlockInfo WoodStairs;
|
||||
public static BlockInfoEx Chest;
|
||||
public static BlockInfo RedstoneWire;
|
||||
public static BlockInfo DiamondOre;
|
||||
public static BlockInfo DiamondBlock;
|
||||
public static BlockInfo CraftTable;
|
||||
public static BlockInfo Crops;
|
||||
public static BlockInfo Farmland;
|
||||
public static BlockInfoEx Furnace;
|
||||
public static BlockInfoEx BurningFurnace;
|
||||
public static BlockInfoEx SignPost;
|
||||
public static BlockInfo WoodDoor;
|
||||
public static BlockInfo Ladder;
|
||||
public static BlockInfo Rails;
|
||||
public static BlockInfo CobbleStairs;
|
||||
public static BlockInfoEx WallSign;
|
||||
public static BlockInfo Lever;
|
||||
public static BlockInfo StonePlate;
|
||||
public static BlockInfo IronDoor;
|
||||
public static BlockInfo WoodPlate;
|
||||
public static BlockInfo RedstoneOre;
|
||||
public static BlockInfo GlowRedstoneOre;
|
||||
public static BlockInfo RedstoneTorch;
|
||||
public static BlockInfo RedstoneTorchOn;
|
||||
public static BlockInfo StoneButton;
|
||||
public static BlockInfo Snow;
|
||||
public static BlockInfo Ice;
|
||||
public static BlockInfo SnowBlock;
|
||||
public static BlockInfo Cactus;
|
||||
public static BlockInfo ClayBlock;
|
||||
public static BlockInfo SugarCane;
|
||||
public static BlockInfo Jukebox;
|
||||
public static BlockInfo Fence;
|
||||
public static BlockInfo Pumpkin;
|
||||
public static BlockInfo Netherrack;
|
||||
public static BlockInfo SoulSand;
|
||||
public static BlockInfo Glowstone;
|
||||
public static BlockInfo Portal;
|
||||
public static BlockInfo JackOLantern;
|
||||
public static BlockInfo CakeBlock;
|
||||
public static BlockInfo RedstoneRepeater;
|
||||
public static BlockInfo RedstoneRepeaterOn;
|
||||
|
||||
static BlockInfo ()
|
||||
{
|
||||
_blockTable = new BlockInfo[MAX_BLOCKS];
|
||||
_opacityTable = new int[MAX_BLOCKS];
|
||||
_luminanceTable = new int[MAX_BLOCKS];
|
||||
|
||||
BlockTable = new ItemCache<BlockInfo>(_blockTable);
|
||||
OpacityTable = new ItemCache<int>(_opacityTable);
|
||||
LuminanceTable = new ItemCache<int>(_luminanceTable);
|
||||
|
||||
Air = new BlockInfo(0, "Air").SetOpacity(0);
|
||||
Stone = new BlockInfo(1, "Stone");
|
||||
Grass = new BlockInfo(2, "Grass");
|
||||
Dirt = new BlockInfo(3, "Dirt");
|
||||
Cobblestone = new BlockInfo(4, "Cobblestone");
|
||||
WoodPlank = new BlockInfo(5, "Wooden Plank");
|
||||
Sapling = new BlockInfo(6, "Sapling").SetOpacity(0);
|
||||
Bedrock = new BlockInfo(7, "Bedrock");
|
||||
Water = new BlockInfo(8, "Water").SetOpacity(3);
|
||||
StationaryWater = new BlockInfo(9, "Stationary Water").SetOpacity(3);
|
||||
Lava = new BlockInfo(10, "Lava").SetLuminance(MAX_LUMINANCE);
|
||||
StationaryLava = new BlockInfo(11, "Stationary Lava").SetLuminance(MAX_LUMINANCE);
|
||||
Sand = new BlockInfo(12, "Sand");
|
||||
Gravel = new BlockInfo(13, "Gravel");
|
||||
GoldOre = new BlockInfo(14, "Gold Ore");
|
||||
IronOre = new BlockInfo(15, "Iron Ore");
|
||||
CoalOre = new BlockInfo(16, "Coal Ore");
|
||||
Wood = new BlockInfo(17, "Wood");
|
||||
Leaves = new BlockInfo(18, "Leaves").SetOpacity(1);
|
||||
Sponge = new BlockInfo(19, "Sponge");
|
||||
Glass = new BlockInfo(20, "Glass").SetOpacity(0);
|
||||
LapisOre = new BlockInfo(21, "Lapis Lazuli Ore");
|
||||
LapisBlock = new BlockInfo(22, "Lapis Lazuli Block");
|
||||
Dispenser = new BlockInfoEx(23, "Dispenser");
|
||||
Sandstone = new BlockInfo(24, "Sandstone");
|
||||
NoteBlock = new BlockInfoEx(25, "Note Block");
|
||||
Bed = new BlockInfo(26, "Bed").SetOpacity(0);
|
||||
Wool = new BlockInfo(35, "Wool");
|
||||
YellowFlower = new BlockInfo(37, "Yellow Flower").SetOpacity(0);
|
||||
RedRose = new BlockInfo(38, "Red Rose").SetOpacity(0);
|
||||
BrownMushroom = new BlockInfo(39, "Brown Mushroom").SetOpacity(0).SetLuminance(1);
|
||||
RedMushroom = new BlockInfo(40, "Red Mushroom").SetOpacity(0).SetLuminance(1);
|
||||
GoldBlock = new BlockInfo(41, "Gold Block");
|
||||
IronBlock = new BlockInfo(42, "Iron Block");
|
||||
DoubleSlab = new BlockInfo(43, "Double Slab");
|
||||
Slab = new BlockInfo(44, "Slab");
|
||||
BrickBlock = new BlockInfo(45, "Brick Block");
|
||||
TNT = new BlockInfo(46, "TNT");
|
||||
Bookshelf = new BlockInfo(47, "Bookshelf");
|
||||
MossStone = new BlockInfo(48, "Moss Stone");
|
||||
Obsidian = new BlockInfo(49, "Obsidian");
|
||||
Torch = new BlockInfo(50, "Torch").SetOpacity(0).SetLuminance(MAX_LUMINANCE - 1);
|
||||
Fire = new BlockInfo(51, "Fire").SetOpacity(0).SetLuminance(MAX_LUMINANCE);
|
||||
MonsterSpawner = (BlockInfoEx)new BlockInfoEx(52, "Monster Spawner").SetOpacity(0);
|
||||
WoodStairs = new BlockInfo(53, "Wooden Stairs").SetOpacity(0);
|
||||
Chest = new BlockInfoEx(54, "Chest");
|
||||
RedstoneWire = new BlockInfo(55, "Redstone Wire").SetOpacity(0);
|
||||
DiamondOre = new BlockInfo(56, "Diamond Ore");
|
||||
DiamondBlock = new BlockInfo(57, "Diamond Block");
|
||||
CraftTable = new BlockInfo(58, "Crafting Table");
|
||||
Crops = new BlockInfo(59, "Crops").SetOpacity(0);
|
||||
Farmland = new BlockInfo(60, "Farmland").SetOpacity(0);
|
||||
Furnace = new BlockInfoEx(61, "Furnace");
|
||||
BurningFurnace = (BlockInfoEx)new BlockInfoEx(62, "Burning Furnace").SetLuminance(MAX_LUMINANCE - 1);
|
||||
SignPost = (BlockInfoEx)new BlockInfoEx(63, "Sign Post").SetOpacity(0);
|
||||
WoodDoor = new BlockInfo(64, "Wooden Door").SetOpacity(0);
|
||||
Ladder = new BlockInfo(65, "Ladder").SetOpacity(0);
|
||||
Rails = new BlockInfo(66, "Rails").SetOpacity(0);
|
||||
CobbleStairs = new BlockInfo(67, "Cobblestone Stairs").SetOpacity(0);
|
||||
WallSign = (BlockInfoEx)new BlockInfoEx(68, "Wall Sign").SetOpacity(0);
|
||||
Lever = new BlockInfo(69, "Lever").SetOpacity(0);
|
||||
StonePlate = new BlockInfo(70, "Stone Pressure Plate").SetOpacity(0);
|
||||
IronDoor = new BlockInfo(71, "Iron Door").SetOpacity(0);
|
||||
WoodPlate = new BlockInfo(72, "Wooden Pressure Plate").SetOpacity(0);
|
||||
RedstoneOre = new BlockInfo(73, "Redstone Ore");
|
||||
GlowRedstoneOre = new BlockInfo(74, "Glowing Redstone Ore").SetLuminance(9);
|
||||
RedstoneTorch = new BlockInfo(75, "Redstone Torch (Off)").SetOpacity(0);
|
||||
RedstoneTorchOn = new BlockInfo(76, "Redstone Torch (On)").SetOpacity(0).SetLuminance(7);
|
||||
StoneButton = new BlockInfo(77, "Stone Button").SetOpacity(0);
|
||||
Snow = new BlockInfo(78, "Snow").SetOpacity(0);
|
||||
Ice = new BlockInfo(79, "Ice").SetOpacity(3);
|
||||
SnowBlock = new BlockInfo(80, "Snow Block");
|
||||
Cactus = new BlockInfo(81, "Cactus").SetOpacity(0);
|
||||
ClayBlock = new BlockInfo(82, "Clay Block");
|
||||
SugarCane = new BlockInfo(83, "Sugar Cane").SetOpacity(0);
|
||||
Jukebox = new BlockInfo(84, "Jukebox");
|
||||
Fence = new BlockInfo(85, "Fence").SetOpacity(0);
|
||||
Pumpkin = new BlockInfo(86, "Pumpkin");
|
||||
Netherrack = new BlockInfo(87, "Netherrack");
|
||||
SoulSand = new BlockInfo(88, "Soul Sand");
|
||||
Glowstone = new BlockInfo(89, "Glowstone Block").SetLuminance(MAX_LUMINANCE);
|
||||
Portal = new BlockInfo(90, "Portal").SetOpacity(0).SetLuminance(11);
|
||||
JackOLantern = new BlockInfo(91, "Jack-O-Lantern").SetLuminance(MAX_LUMINANCE);
|
||||
CakeBlock = new BlockInfo(92, "Cake Block").SetOpacity(0);
|
||||
RedstoneRepeater = new BlockInfo(93, "Redstone Repeater (Off)").SetOpacity(0);
|
||||
RedstoneRepeaterOn = new BlockInfo(94, "Redstone Repeater (On)").SetOpacity(0).SetLuminance(7);
|
||||
|
||||
for (int i = 0; i < MAX_BLOCKS; i++) {
|
||||
if (_blockTable[i] == null) {
|
||||
_blockTable[i] = new BlockInfo(i, "Uknown Block");
|
||||
}
|
||||
}
|
||||
|
||||
// Set Tile Entity Data
|
||||
|
||||
Dispenser.SetTileEntity("Trap");
|
||||
NoteBlock.SetTileEntity("Music");
|
||||
MonsterSpawner.SetTileEntity("MobSpawner");
|
||||
Chest.SetTileEntity("Chest");
|
||||
Furnace.SetTileEntity("Furnace");
|
||||
BurningFurnace.SetTileEntity("Furnace");
|
||||
SignPost.SetTileEntity("Sign");
|
||||
WallSign.SetTileEntity("Sign");
|
||||
|
||||
// Set Data Limits
|
||||
|
||||
Wood.SetDataLimits(0, 2, 0);
|
||||
Leaves.SetDataLimits(0, 2, 0);
|
||||
Jukebox.SetDataLimits(0, 2, 0);
|
||||
Sapling.SetDataLimits(0, 15, 0);
|
||||
Cactus.SetDataLimits(0, 15, 0);
|
||||
SugarCane.SetDataLimits(0, 15, 0);
|
||||
Water.SetDataLimits(0, 7, 0x8);
|
||||
Lava.SetDataLimits(0, 7, 0x8);
|
||||
Crops.SetDataLimits(0, 7, 0);
|
||||
Wool.SetDataLimits(0, 15, 0);
|
||||
Torch.SetDataLimits(1, 5, 0);
|
||||
RedstoneTorch.SetDataLimits(0, 5, 0);
|
||||
RedstoneTorchOn.SetDataLimits(0, 5, 0);
|
||||
Rails.SetDataLimits(0, 9, 0);
|
||||
Ladder.SetDataLimits(2, 5, 0);
|
||||
WoodStairs.SetDataLimits(0, 3, 0);
|
||||
CobbleStairs.SetDataLimits(0, 3, 0);
|
||||
Lever.SetDataLimits(0, 6, 0x8);
|
||||
WoodDoor.SetDataLimits(0, 3, 0xC);
|
||||
IronDoor.SetDataLimits(0, 3, 0xC);
|
||||
StoneButton.SetDataLimits(1, 4, 0x8);
|
||||
SignPost.SetDataLimits(0, 15, 0);
|
||||
WallSign.SetDataLimits(2, 5, 0);
|
||||
Furnace.SetDataLimits(2, 5, 0);
|
||||
BurningFurnace.SetDataLimits(2, 5, 0);
|
||||
Dispenser.SetDataLimits(2, 5, 0);
|
||||
Pumpkin.SetDataLimits(0, 3, 0);
|
||||
JackOLantern.SetDataLimits(0, 3, 0);
|
||||
StonePlate.SetDataLimits(0, 0, 0x1);
|
||||
WoodPlate.SetDataLimits(0, 0, 0x1);
|
||||
Slab.SetDataLimits(0, 3, 0);
|
||||
DoubleSlab.SetDataLimits(0, 3, 0);
|
||||
Cactus.SetDataLimits(0, 5, 0);
|
||||
Bed.SetDataLimits(0, 3, 0x8);
|
||||
RedstoneRepeater.SetDataLimits(0, 0, 0xF);
|
||||
RedstoneRepeaterOn.SetDataLimits(0, 0, 0xF);
|
||||
}
|
||||
}
|
||||
|
||||
public class BlockInfoEx : BlockInfo
|
||||
{
|
||||
private string _tileEntityName;
|
||||
|
||||
public string TileEntityName
|
||||
{
|
||||
get { return _tileEntityName; }
|
||||
}
|
||||
|
||||
public BlockInfoEx (int id) : base(id) { }
|
||||
|
||||
public BlockInfoEx (int id, string name) : base(id, name) { }
|
||||
|
||||
public BlockInfo SetTileEntity (string name) {
|
||||
_tileEntityName = name;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
62
Substrate/SubstrateCS/Source/BlockInterface.cs
Normal file
62
Substrate/SubstrateCS/Source/BlockInterface.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
|
||||
public interface IBlock
|
||||
{
|
||||
BlockInfo Info { get; }
|
||||
int ID { get; set; }
|
||||
int Data { get; set; }
|
||||
int BlockLight { get; set; }
|
||||
int SkyLight { get; set; }
|
||||
|
||||
TileEntity GetTileEntity ();
|
||||
bool SetTileEntity (TileEntity te);
|
||||
bool ClearTileEntity ();
|
||||
}
|
||||
|
||||
public interface IBlockContainer
|
||||
{
|
||||
int BlockGlobalX (int x);
|
||||
int BlockGlobalY (int y);
|
||||
int BlockGlobalZ (int z);
|
||||
|
||||
int BlockLocalX (int x);
|
||||
int BlockLocalY (int y);
|
||||
int BlockLocalZ (int z);
|
||||
|
||||
Block GetBlock (int lx, int ly, int lz);
|
||||
BlockRef GetBlockRef (int lx, int ly, int lz);
|
||||
|
||||
BlockInfo GetBlockInfo (int lx, int ly, int lz);
|
||||
|
||||
int GetBlockID (int lx, int ly, int lz);
|
||||
int GetBlockData (int lx, int ly, int lz);
|
||||
int GetBlockLight (int lx, int ly, int lz);
|
||||
int GetBlockSkyLight (int lx, int ly, int lz);
|
||||
|
||||
void SetBlock (int lx, int ly, int lz, Block block);
|
||||
|
||||
bool SetBlockID (int lx, int ly, int lz, int id);
|
||||
bool SetBlockData (int lx, int ly, int lz, int data);
|
||||
bool SetBlockLight (int lx, int ly, int lz, int light);
|
||||
bool SetBlockSkyLight (int lx, int ly, int lz, int light);
|
||||
|
||||
TileEntity GetTileEntity (int lx, int ly, int lz);
|
||||
bool SetTileEntity (int lx, int ly, int lz, TileEntity te);
|
||||
bool ClearTileEntity (int lx, int ly, int lz);
|
||||
}
|
||||
|
||||
public interface IEntity
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public interface IEntityCollection
|
||||
{
|
||||
List<Entity> FindAll (Predicate<Entity> match);
|
||||
}
|
||||
}
|
54
Substrate/SubstrateCS/Source/BlockKey.cs
Normal file
54
Substrate/SubstrateCS/Source/BlockKey.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
public struct BlockKey : IEquatable<BlockKey>
|
||||
{
|
||||
readonly int x;
|
||||
readonly int y;
|
||||
readonly int z;
|
||||
|
||||
public BlockKey (int _x, int _y, int _z)
|
||||
{
|
||||
x = _x;
|
||||
y = _y;
|
||||
z = _z;
|
||||
}
|
||||
|
||||
public bool Equals (BlockKey bk)
|
||||
{
|
||||
return this.x == bk.x && this.y == bk.y && this.z == bk.z;
|
||||
}
|
||||
|
||||
public override bool Equals (Object o)
|
||||
{
|
||||
try {
|
||||
return this == (BlockKey)o;
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode ()
|
||||
{
|
||||
int hash = 23;
|
||||
hash = hash * 37 + x;
|
||||
hash = hash * 37 + y;
|
||||
hash = hash * 37 + z;
|
||||
return hash;
|
||||
}
|
||||
|
||||
public static bool operator == (BlockKey k1, BlockKey k2)
|
||||
{
|
||||
return k1.x == k2.x && k1.y == k2.y && k1.z == k2.z;
|
||||
}
|
||||
|
||||
public static bool operator != (BlockKey k1, BlockKey k2)
|
||||
{
|
||||
return k1.x != k2.x || k1.y != k2.y || k1.z != k2.z;
|
||||
}
|
||||
}
|
||||
}
|
251
Substrate/SubstrateCS/Source/BlockManager.cs
Normal file
251
Substrate/SubstrateCS/Source/BlockManager.cs
Normal file
|
@ -0,0 +1,251 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
/*public interface IBlockManager : IBlockContainer
|
||||
{
|
||||
Block GetBlock (int x, int y, int z);
|
||||
BlockRef GetBlockRef (int x, int y, int z);
|
||||
BlockInfo GetBlockInfo (int x, int y, int z);
|
||||
|
||||
bool SetBlock (int x, int y, int z, Block block);
|
||||
}*/
|
||||
|
||||
public class BlockManager : IBlockContainer
|
||||
{
|
||||
public const int MIN_X = -32000000;
|
||||
public const int MAX_X = 32000000;
|
||||
public const int MIN_Y = 0;
|
||||
public const int MAX_Y = 128;
|
||||
public const int MIN_Z = -32000000;
|
||||
public const int MAX_Z = 32000000;
|
||||
|
||||
public const int CHUNK_XLEN = 16;
|
||||
public const int CHUNK_YLEN = 128;
|
||||
public const int CHUNK_ZLEN = 16;
|
||||
|
||||
public const int CHUNK_XLOG = 4;
|
||||
public const int CHUNK_YLOG = 7;
|
||||
public const int CHUNK_ZLOG = 4;
|
||||
|
||||
public const int CHUNK_XMASK = 0xF;
|
||||
public const int CHUNK_YMASK = 0x7F;
|
||||
public const int CHUNK_ZMASK = 0xF;
|
||||
|
||||
public static bool EnforceDataLimits = true;
|
||||
|
||||
protected ChunkManager _chunkMan;
|
||||
|
||||
protected ChunkRef _cache;
|
||||
|
||||
public BlockManager (ChunkManager cm)
|
||||
{
|
||||
_chunkMan = cm;
|
||||
}
|
||||
|
||||
public BlockManager (BlockManager bm)
|
||||
{
|
||||
_chunkMan = bm._chunkMan;
|
||||
}
|
||||
|
||||
public int BlockGlobalX (int x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
public int BlockGlobalY (int y)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
public int BlockGlobalZ (int z)
|
||||
{
|
||||
return z;
|
||||
}
|
||||
|
||||
public int BlockLocalX (int x)
|
||||
{
|
||||
return x & CHUNK_XMASK;
|
||||
}
|
||||
|
||||
public int BlockLocalY (int y)
|
||||
{
|
||||
return y & CHUNK_YMASK;
|
||||
}
|
||||
|
||||
public int BlockLocalZ (int z)
|
||||
{
|
||||
return z & CHUNK_ZMASK;
|
||||
}
|
||||
|
||||
public virtual Block GetBlock (int x, int y, int z)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null || !Check(x, y, z)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return _cache.GetBlock(x & CHUNK_XMASK, y, z & CHUNK_ZMASK);
|
||||
}
|
||||
|
||||
public virtual BlockRef GetBlockRef (int x, int y, int z)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null || !Check(x, y, z)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return _cache.GetBlockRef(x & CHUNK_XMASK, y, z & CHUNK_ZMASK);
|
||||
}
|
||||
|
||||
public virtual BlockInfo GetBlockInfo (int x, int y, int z)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null || !Check(x, y, z)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return _cache.GetBlockInfo(x & CHUNK_XMASK, y, z & CHUNK_ZMASK);
|
||||
}
|
||||
|
||||
public virtual int GetBlockID (int x, int y, int z)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _cache.GetBlockID(x & CHUNK_XMASK, y, z & CHUNK_ZMASK);
|
||||
}
|
||||
|
||||
public virtual int GetBlockData (int x, int y, int z)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _cache.GetBlockData(x & CHUNK_XMASK, y, z & CHUNK_ZMASK);
|
||||
}
|
||||
|
||||
public virtual int GetBlockLight (int x, int y, int z)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _cache.GetBlockLight(x & CHUNK_XMASK, y, z & CHUNK_ZMASK);
|
||||
}
|
||||
|
||||
public virtual int GetBlockSkyLight (int x, int y, int z)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _cache.GetBlockSkyLight(x & CHUNK_XMASK, y, z & CHUNK_ZMASK);
|
||||
}
|
||||
|
||||
public virtual void SetBlock (int x, int y, int z, Block block)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null || !Check(x, y, z)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_cache.SetBlock(x & CHUNK_XMASK, y, z & CHUNK_ZMASK, block);
|
||||
}
|
||||
|
||||
public virtual bool SetBlockID (int x, int y, int z, int id)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null || !Check(x, y, z)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _cache.SetBlockID(x & CHUNK_XMASK, y, z & CHUNK_ZMASK, id);
|
||||
}
|
||||
|
||||
public virtual bool SetBlockData (int x, int y, int z, int data)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null || !Check(x, y, z)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _cache.SetBlockData(x & CHUNK_XMASK, y, z & CHUNK_ZMASK, data);
|
||||
}
|
||||
|
||||
public bool SetBlockLight (int x, int y, int z, int light)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null || !Check(x, y, z)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _cache.SetBlockID(x & CHUNK_XMASK, y, z & CHUNK_ZMASK, light);
|
||||
}
|
||||
|
||||
public bool SetBlockSkyLight (int x, int y, int z, int light)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null || !Check(x, y, z)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _cache.SetBlockSkyLight(x & CHUNK_XMASK, y, z & CHUNK_ZMASK, light);
|
||||
}
|
||||
|
||||
public virtual TileEntity GetTileEntity (int x, int y, int z)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null || !Check(x, y, z)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return _cache.GetTileEntity(x & CHUNK_XMASK, y, z & CHUNK_ZMASK);
|
||||
}
|
||||
|
||||
public virtual bool SetTileEntity (int x, int y, int z, TileEntity te)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null || !Check(x, y, z)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _cache.SetTileEntity(x & CHUNK_XMASK, y, z & CHUNK_ZMASK, te);
|
||||
}
|
||||
|
||||
public virtual bool ClearTileEntity (int x, int y, int z)
|
||||
{
|
||||
_cache = GetChunk(x, y, z);
|
||||
if (_cache == null || !Check(x, y, z)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _cache.ClearTileEntity(x & CHUNK_XMASK, y, z & CHUNK_ZMASK);
|
||||
}
|
||||
|
||||
protected ChunkRef GetChunk (int x, int y, int z)
|
||||
{
|
||||
x >>= CHUNK_XLOG;
|
||||
z >>= CHUNK_ZLOG;
|
||||
return _chunkMan.GetChunkRef(x, z);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by other block-specific 'get' and 'set' functions to filter
|
||||
/// out operations on some blocks. Override this method in derrived
|
||||
/// classes to filter the entire BlockManager.
|
||||
/// </summary>
|
||||
protected virtual bool Check (int x, int y, int z) {
|
||||
return (x >= MIN_X) && (x < MAX_X) &&
|
||||
(y >= MIN_Y) && (y < MAX_Y) &&
|
||||
(z >= MIN_Z) && (z < MAX_Z);
|
||||
}
|
||||
}
|
||||
}
|
206
Substrate/SubstrateCS/Source/BlockRef.cs
Normal file
206
Substrate/SubstrateCS/Source/BlockRef.cs
Normal file
|
@ -0,0 +1,206 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
public class BlockRef : IBlock
|
||||
{
|
||||
protected IBlockContainer _container;
|
||||
|
||||
protected int _x;
|
||||
protected int _y;
|
||||
protected int _z;
|
||||
|
||||
public int X
|
||||
{
|
||||
get { return _container.BlockGlobalX(_x); }
|
||||
}
|
||||
|
||||
public int Y
|
||||
{
|
||||
get { return _container.BlockGlobalY(_y); }
|
||||
}
|
||||
|
||||
public int Z
|
||||
{
|
||||
get { return _container.BlockGlobalZ(_z); }
|
||||
}
|
||||
|
||||
public int LocalX
|
||||
{
|
||||
get { return _container.BlockLocalX(_x); }
|
||||
}
|
||||
|
||||
public int LocalY
|
||||
{
|
||||
get { return _container.BlockLocalZ(_z); }
|
||||
}
|
||||
|
||||
public int LocalZ
|
||||
{
|
||||
get { return _z; }
|
||||
}
|
||||
|
||||
public BlockInfo Info
|
||||
{
|
||||
get { return BlockInfo.BlockTable[_container.GetBlockID(_x, _y, _z)]; }
|
||||
}
|
||||
|
||||
public int ID
|
||||
{
|
||||
get { return _container.GetBlockID(_x, _y, _z); }
|
||||
set { _container.SetBlockID(_x, _y, _z, value); }
|
||||
}
|
||||
|
||||
public int Data
|
||||
{
|
||||
get { return _container.GetBlockData(_x, _y, _z); }
|
||||
set { _container.SetBlockData(_x, _y, _z, value); }
|
||||
}
|
||||
|
||||
public int BlockLight
|
||||
{
|
||||
get { return _container.GetBlockLight(_x, _y, _z); }
|
||||
set { _container.SetBlockLight(_x, _y, _z, value); }
|
||||
}
|
||||
|
||||
public int SkyLight
|
||||
{
|
||||
get { return _container.GetBlockSkyLight(_x, _y, _z); }
|
||||
set { _container.SetBlockSkyLight(_x, _y, _z, value); }
|
||||
}
|
||||
|
||||
public BlockRef (IBlockContainer container, int x, int y, int z)
|
||||
{
|
||||
_container = container;
|
||||
_x = x;
|
||||
_y = y;
|
||||
_z = z;
|
||||
}
|
||||
|
||||
public void CopyFrom (IBlock block)
|
||||
{
|
||||
ID = block.ID;
|
||||
Data = block.Data;
|
||||
BlockLight = block.BlockLight;
|
||||
SkyLight = block.SkyLight;
|
||||
|
||||
SetTileEntity(block.GetTileEntity().Copy());
|
||||
}
|
||||
|
||||
public TileEntity GetTileEntity ()
|
||||
{
|
||||
return _container.GetTileEntity(_x, _y, _z);
|
||||
}
|
||||
|
||||
public bool SetTileEntity (TileEntity te)
|
||||
{
|
||||
return _container.SetTileEntity(_x, _y, _z, te);
|
||||
}
|
||||
|
||||
public bool ClearTileEntity ()
|
||||
{
|
||||
return _container.ClearTileEntity(_x, _y, _z);
|
||||
}
|
||||
}
|
||||
|
||||
/*public class BlockRef : IBlock
|
||||
{
|
||||
protected IChunk _chunk;
|
||||
|
||||
protected int _lx;
|
||||
protected int _ly;
|
||||
protected int _lz;
|
||||
|
||||
public int X
|
||||
{
|
||||
get { return _lx + (_chunk.X * BlockManager.CHUNK_XLEN); }
|
||||
}
|
||||
|
||||
public int Y
|
||||
{
|
||||
get { return _ly; }
|
||||
}
|
||||
|
||||
public int Z
|
||||
{
|
||||
get { return _lz + (_chunk.Z * BlockManager.CHUNK_ZLEN); }
|
||||
}
|
||||
|
||||
public int LocalX
|
||||
{
|
||||
get { return _lx; }
|
||||
}
|
||||
|
||||
public int LocalY
|
||||
{
|
||||
get { return _ly; }
|
||||
}
|
||||
|
||||
public int LocalZ
|
||||
{
|
||||
get { return _lz; }
|
||||
}
|
||||
|
||||
public BlockInfo Info
|
||||
{
|
||||
get { return BlockInfo.BlockTable[_chunk.GetBlockID(_lx, _ly, _lz)]; }
|
||||
}
|
||||
|
||||
public int ID
|
||||
{
|
||||
get { return _chunk.GetBlockID(_lx, _ly, _lz); }
|
||||
set { _chunk.SetBlockID(_lx, _ly, _lz, value); }
|
||||
}
|
||||
|
||||
public int Data
|
||||
{
|
||||
get { return _chunk.GetBlockData(_lx, _ly, _lz); }
|
||||
set { _chunk.SetBlockData(_lx, _ly, _lz, value); }
|
||||
}
|
||||
|
||||
public int BlockLight
|
||||
{
|
||||
get { return _chunk.GetBlockLight(_lx, _ly, _lz); }
|
||||
set { _chunk.SetBlockLight(_lx, _ly, _lz, value); }
|
||||
}
|
||||
|
||||
public int SkyLight
|
||||
{
|
||||
get { return _chunk.GetBlockSkyLight(_lx, _ly, _lz); }
|
||||
set { _chunk.SetBlockSkyLight(_lx, _ly, _lz, value); }
|
||||
}
|
||||
|
||||
public BlockRef (IChunk c, int lx, int ly, int lz)
|
||||
{
|
||||
_chunk = c;
|
||||
_lx = lx;
|
||||
_ly = ly;
|
||||
_lz = lz;
|
||||
}
|
||||
|
||||
public void CopyFrom (IBlock block)
|
||||
{
|
||||
ID = block.ID;
|
||||
Data = block.Data;
|
||||
BlockLight = block.BlockLight;
|
||||
SkyLight = block.SkyLight;
|
||||
}
|
||||
|
||||
public TileEntity GetTileEntity ()
|
||||
{
|
||||
return _chunk.GetTileEntity(_lx, _ly, _lz);
|
||||
}
|
||||
|
||||
public bool SetTileEntity (TileEntity te)
|
||||
{
|
||||
return _chunk.SetTileEntity(_lx, _ly, _lz, te);
|
||||
}
|
||||
|
||||
public bool ClearTileEntity ()
|
||||
{
|
||||
return _chunk.ClearTileEntity(_lx, _ly, _lz);
|
||||
}
|
||||
}*/
|
||||
}
|
524
Substrate/SubstrateCS/Source/Chunk.cs
Normal file
524
Substrate/SubstrateCS/Source/Chunk.cs
Normal file
|
@ -0,0 +1,524 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
using NBT;
|
||||
using Utility;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class Chunk : IChunk, INBTObject<Chunk>, ICopyable<Chunk>
|
||||
{
|
||||
public static NBTCompoundNode LevelSchema = new NBTCompoundNode()
|
||||
{
|
||||
new NBTCompoundNode("Level")
|
||||
{
|
||||
new NBTArrayNode("Blocks", 32768),
|
||||
new NBTArrayNode("Data", 16384),
|
||||
new NBTArrayNode("SkyLight", 16384),
|
||||
new NBTArrayNode("BlockLight", 16384),
|
||||
new NBTArrayNode("HeightMap", 256),
|
||||
new NBTListNode("Entities", NBT_Type.TAG_COMPOUND),
|
||||
new NBTListNode("TileEntities", NBT_Type.TAG_COMPOUND, TileEntity.BaseSchema),
|
||||
new NBTScalerNode("LastUpdate", NBT_Type.TAG_LONG),
|
||||
new NBTScalerNode("xPos", NBT_Type.TAG_INT),
|
||||
new NBTScalerNode("zPos", NBT_Type.TAG_INT),
|
||||
new NBTScalerNode("TerrainPopulated", NBT_Type.TAG_BYTE),
|
||||
},
|
||||
};
|
||||
|
||||
private NBT_Tree _tree;
|
||||
|
||||
private int _cx;
|
||||
private int _cz;
|
||||
|
||||
protected NBT_ByteArray _blocks;
|
||||
protected NibbleArray _data;
|
||||
protected NibbleArray _blockLight;
|
||||
protected NibbleArray _skyLight;
|
||||
protected NBT_ByteArray _heightMap;
|
||||
|
||||
protected NBT_List _entities;
|
||||
protected NBT_List _tileEntities;
|
||||
|
||||
protected Dictionary<BlockKey, NBT_Compound> _tileEntityTable;
|
||||
|
||||
public int X
|
||||
{
|
||||
get { return _cx; }
|
||||
}
|
||||
|
||||
public int Z
|
||||
{
|
||||
get { return _cz; }
|
||||
}
|
||||
|
||||
public NBT_Tree Tree
|
||||
{
|
||||
get { return _tree; }
|
||||
}
|
||||
|
||||
public bool IsTerrainPopulated
|
||||
{
|
||||
get { return _tree.Root["Level"].ToNBTCompound()["TerrainPopulated"].ToNBTByte() == 1; }
|
||||
set { _tree.Root["Level"].ToNBTCompound()["TerrainPopulated"].ToNBTByte().Data = (byte)(value ? 1 : 0); }
|
||||
}
|
||||
|
||||
public Chunk (int x, int z)
|
||||
{
|
||||
_cx = x;
|
||||
_cz = z;
|
||||
|
||||
BuildNBTTree();
|
||||
|
||||
BuildTileEntityCache();
|
||||
}
|
||||
|
||||
public Chunk (NBT_Tree tree)
|
||||
{
|
||||
if (LoadTreeSafe(tree.Root) == null) {
|
||||
throw new InvalidNBTObjectException();
|
||||
}
|
||||
}
|
||||
|
||||
private void BuildNBTTree ()
|
||||
{
|
||||
int elements2 = BlockManager.CHUNK_XLEN * BlockManager.CHUNK_ZLEN;
|
||||
int elements3 = elements2 * BlockManager.CHUNK_YLEN;
|
||||
|
||||
_blocks = new NBT_ByteArray(new byte[elements3]);
|
||||
NBT_ByteArray data = new NBT_ByteArray(new byte[elements3 >> 1]);
|
||||
NBT_ByteArray blocklight = new NBT_ByteArray(new byte[elements3 >> 1]);
|
||||
NBT_ByteArray skylight = new NBT_ByteArray(new byte[elements3 >> 1]);
|
||||
_heightMap = new NBT_ByteArray(new byte[elements2]);
|
||||
_entities = new NBT_List(NBT_Type.TAG_COMPOUND);
|
||||
_tileEntities = new NBT_List(NBT_Type.TAG_COMPOUND);
|
||||
|
||||
_data = new NibbleArray(data.Data);
|
||||
_blockLight = new NibbleArray(blocklight.Data);
|
||||
_skyLight = new NibbleArray(skylight.Data);
|
||||
|
||||
NBT_Compound level = new NBT_Compound();
|
||||
level.Add("Blocks", _blocks);
|
||||
level.Add("Data", data);
|
||||
level.Add("SkyLight", blocklight);
|
||||
level.Add("BlockLight", skylight);
|
||||
level.Add("HeightMap", _heightMap);
|
||||
level.Add("Entities", _entities);
|
||||
level.Add("TileEntities", _tileEntities);
|
||||
level.Add("LastUpdate", new NBT_Long());
|
||||
level.Add("xPos", new NBT_Int());
|
||||
level.Add("zPos", new NBT_Int());
|
||||
level.Add("TerrainPopulated", new NBT_Byte());
|
||||
|
||||
_tree = new NBT_Tree();
|
||||
_tree.Root.Add("Level", level);
|
||||
}
|
||||
|
||||
public int BlockGlobalX (int x)
|
||||
{
|
||||
return _cx * BlockManager.CHUNK_XLEN + x;
|
||||
}
|
||||
|
||||
public int BlockGlobalY (int y)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
public int BlockGlobalZ (int z)
|
||||
{
|
||||
return _cz * BlockManager.CHUNK_ZLEN + z;
|
||||
}
|
||||
|
||||
public int BlockLocalX (int x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
public int BlockLocalY (int y)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
public int BlockLocalZ (int z)
|
||||
{
|
||||
return z;
|
||||
}
|
||||
|
||||
public bool Save (Stream outStream)
|
||||
{
|
||||
if (outStream == null || !outStream.CanWrite) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_tree.WriteTo(outStream);
|
||||
outStream.Close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Block GetBlock (int lx, int ly, int lz)
|
||||
{
|
||||
return new Block(this, lx, ly, lz);
|
||||
}
|
||||
|
||||
public BlockRef GetBlockRef (int lx, int ly, int lz)
|
||||
{
|
||||
return new BlockRef(this, lx, ly, lz);
|
||||
}
|
||||
|
||||
public BlockInfo GetBlockInfo (int lx, int ly, int lz)
|
||||
{
|
||||
return BlockInfo.BlockTable[GetBlockID(lx, ly, lz)];
|
||||
}
|
||||
|
||||
public void SetBlock (int lx, int ly, int lz, Block block)
|
||||
{
|
||||
int index = lx << 11 | lz << 7 | ly;
|
||||
|
||||
SetBlockID(lx, ly, lz, block.ID);
|
||||
SetBlockData(lx, ly, lz, block.Data);
|
||||
|
||||
_blockLight[index] = block.BlockLight;
|
||||
_skyLight[index] = block.SkyLight;
|
||||
|
||||
SetTileEntity(lx, ly, lz, block.GetTileEntity().Copy());
|
||||
}
|
||||
|
||||
public int GetBlockID (int lx, int ly, int lz)
|
||||
{
|
||||
return _blocks.Data[lx << 11 | lz << 7 | ly];
|
||||
}
|
||||
|
||||
public int GetBlockData (int lx, int ly, int lz)
|
||||
{
|
||||
return _data[lx << 11 | lz << 7 | ly];
|
||||
}
|
||||
|
||||
public int GetBlockLight (int lx, int ly, int lz)
|
||||
{
|
||||
return _blockLight[lx << 11 | lz << 7 | ly];
|
||||
}
|
||||
|
||||
public int GetBlockSkyLight (int lx, int ly, int lz)
|
||||
{
|
||||
return _skyLight[lx << 11 | lz << 7 | ly];
|
||||
}
|
||||
|
||||
public bool SetBlockID (int lx, int ly, int lz, int id)
|
||||
{
|
||||
int index = lx << 11 | lz << 7 | ly;
|
||||
int oldid = _blocks.Data[index];
|
||||
|
||||
if (oldid == id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update value
|
||||
|
||||
_blocks.Data[index] = (byte)id;
|
||||
|
||||
// Update tile entities
|
||||
|
||||
BlockInfoEx info1 = BlockInfo.BlockTable[oldid] as BlockInfoEx;
|
||||
BlockInfoEx info2 = BlockInfo.BlockTable[id] as BlockInfoEx;
|
||||
|
||||
if (info1 != info2) {
|
||||
if (info1 != null) {
|
||||
ClearTileEntity(lx, ly, lz);
|
||||
}
|
||||
|
||||
if (info2 != null) {
|
||||
CreateTileEntity(lx, ly, lz);
|
||||
}
|
||||
}
|
||||
|
||||
/*if (BlockInfo.SchemaTable[_blocks[index]] != BlockInfo.SchemaTable[id]) {
|
||||
if (BlockInfo.SchemaTable[_blocks[index]] != null) {
|
||||
ClearTileEntity(lx, ly, lz);
|
||||
}
|
||||
|
||||
if (BlockInfo.SchemaTable[id] != null) {
|
||||
TileEntity te = new TileEntity(BlockInfo.SchemaTable[id]);
|
||||
te.X = BlockGlobalX(lx);
|
||||
te.Y = BlockGlobalY(ly);
|
||||
te.Z = BlockGlobalZ(lz);
|
||||
_tileEntities.Add(te.Root);
|
||||
}
|
||||
}*/
|
||||
|
||||
// Update height map
|
||||
|
||||
if (BlockInfo.BlockTable[id] != null) {
|
||||
int tileHeight = GetHeight(lx, lz);
|
||||
int newOpacity = BlockInfo.BlockTable[id].Opacity;
|
||||
|
||||
if (ly > tileHeight && newOpacity > BlockInfo.MIN_OPACITY) {
|
||||
_heightMap[lz << 4 | lx] = (byte)ly;
|
||||
}
|
||||
else if (ly == tileHeight && newOpacity == BlockInfo.MIN_OPACITY) {
|
||||
for (int i = ly - 1; i >= 0; i--) {
|
||||
if (BlockInfo.BlockTable[GetBlockID(lx, i, lz)].Opacity > BlockInfo.MIN_OPACITY) {
|
||||
_heightMap[lz << 4 | lx] = (byte)i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetBlockData (int lx, int ly, int lz, int data)
|
||||
{
|
||||
int index = lx << 11 | lz << 7 | ly;
|
||||
if (_data[index] == data) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BlockManager.EnforceDataLimits && BlockInfo.BlockTable[_blocks[index]] != null) {
|
||||
if (!BlockInfo.BlockTable[_blocks[index]].TestData(data)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_data[index] = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetBlockLight (int lx, int ly, int lz, int light)
|
||||
{
|
||||
int index = lx << 11 | lz << 7 | ly;
|
||||
if (_blockLight[index] == light) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_blockLight[index] = light;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool SetBlockSkyLight (int lx, int ly, int lz, int light)
|
||||
{
|
||||
int index = lx << 11 | lz << 7 | ly;
|
||||
if (_skyLight[index] == light) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_skyLight[index] = light;
|
||||
return true;
|
||||
}
|
||||
|
||||
public int CountBlockID (int id)
|
||||
{
|
||||
int c = 0;
|
||||
for (int i = 0; i < _blocks.Length; i++) {
|
||||
if (_blocks[i] == id) {
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public int CountBlockData (int id, int data)
|
||||
{
|
||||
int c = 0;
|
||||
for (int i = 0; i < _blocks.Length; i++) {
|
||||
if (_blocks[i] == id && _data[i] == data) {
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public int GetHeight (int lx, int lz)
|
||||
{
|
||||
return _heightMap[lz << 4 | lx];
|
||||
}
|
||||
|
||||
private void CreateTileEntity (int lx, int ly, int lz)
|
||||
{
|
||||
BlockInfoEx info = GetBlockInfo(lx, ly, lz) as BlockInfoEx;
|
||||
if (info == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TileEntity te = TileEntityFactory.Create(info.TileEntityName);
|
||||
if (te == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
te.X = BlockGlobalX(lx);
|
||||
te.Y = BlockGlobalY(ly);
|
||||
te.Z = BlockGlobalZ(lz);
|
||||
|
||||
_tileEntities.Add(te.BuildTree());
|
||||
}
|
||||
|
||||
public TileEntity GetTileEntity (int lx, int ly, int lz)
|
||||
{
|
||||
int x = BlockGlobalX(lx);
|
||||
int y = BlockGlobalY(ly);
|
||||
int z = BlockGlobalZ(lz);
|
||||
|
||||
BlockKey key = new BlockKey(x, y, z);
|
||||
NBT_Compound te;
|
||||
|
||||
if (!_tileEntityTable.TryGetValue(key, out te)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return TileEntityFactory.Create(te);
|
||||
}
|
||||
|
||||
public bool SetTileEntity (int lx, int ly, int lz, TileEntity te)
|
||||
{
|
||||
BlockInfoEx info = GetBlockInfo(lx, ly, lz) as BlockInfoEx;
|
||||
if (info == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (te.GetType() != TileEntityFactory.Lookup(info.TileEntityName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int x = BlockGlobalX(lx);
|
||||
int y = BlockGlobalY(ly);
|
||||
int z = BlockGlobalZ(lz);
|
||||
|
||||
BlockKey key = new BlockKey(x, y, z);
|
||||
NBT_Compound oldte;
|
||||
|
||||
if (_tileEntityTable.TryGetValue(key, out oldte)) {
|
||||
_tileEntities.Remove(oldte);
|
||||
}
|
||||
|
||||
te.X = x;
|
||||
te.Y = y;
|
||||
te.Z = z;
|
||||
|
||||
NBT_Compound tree = te.BuildTree() as NBT_Compound;
|
||||
|
||||
_tileEntities.Add(tree);
|
||||
_tileEntityTable[key] = tree;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ClearTileEntity (int lx, int ly, int lz)
|
||||
{
|
||||
int x = BlockGlobalX(lx);
|
||||
int y = BlockGlobalY(ly);
|
||||
int z = BlockGlobalZ(lz);
|
||||
|
||||
BlockKey key = new BlockKey(x, y, z);
|
||||
NBT_Compound te;
|
||||
|
||||
if (!_tileEntityTable.TryGetValue(key, out te)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_tileEntities.Remove(te);
|
||||
_tileEntityTable.Remove(key);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual void SetLocation (int x, int z)
|
||||
{
|
||||
int diffx = x - _cx;
|
||||
int diffz = z - _cz;
|
||||
|
||||
_cx = x;
|
||||
_cz = z;
|
||||
|
||||
BuildTileEntityCache();
|
||||
}
|
||||
|
||||
private void BuildTileEntityCache ()
|
||||
{
|
||||
_tileEntityTable = new Dictionary<BlockKey, NBT_Compound>();
|
||||
|
||||
foreach (NBT_Compound te in _tileEntities) {
|
||||
int tex = te["x"].ToNBTInt();
|
||||
int tey = te["y"].ToNBTInt();
|
||||
int tez = te["z"].ToNBTInt();
|
||||
|
||||
BlockKey key = new BlockKey(tex, tey, tez);
|
||||
_tileEntityTable[key] = te;
|
||||
}
|
||||
}
|
||||
|
||||
#region ICopyable<Chunk> Members
|
||||
|
||||
public Chunk Copy ()
|
||||
{
|
||||
return new Chunk(_tree.Copy());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INBTObject<Chunk> Members
|
||||
|
||||
public Chunk LoadTree (NBT_Value tree)
|
||||
{
|
||||
NBT_Compound ctree = tree as NBT_Compound;
|
||||
if (ctree == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_tree = new NBT_Tree(ctree);
|
||||
|
||||
NBT_Compound level = _tree.Root["Level"] as NBT_Compound;
|
||||
|
||||
_blocks = level["Blocks"] as NBT_ByteArray;
|
||||
_data = new NibbleArray(level["Data"].ToNBTByteArray().Data);
|
||||
_blockLight = new NibbleArray(level["BlockLight"].ToNBTByteArray().Data);
|
||||
_skyLight = new NibbleArray(level["SkyLight"].ToNBTByteArray().Data);
|
||||
_heightMap = level["HeightMap"] as NBT_ByteArray;
|
||||
|
||||
_entities = level["Entities"] as NBT_List;
|
||||
_tileEntities = level["TileEntities"] as NBT_List;
|
||||
|
||||
// List-type patch up
|
||||
if (_entities.Count == 0) {
|
||||
level["Entities"] = new NBT_List(NBT_Type.TAG_COMPOUND);
|
||||
_entities = level["Entities"] as NBT_List;
|
||||
}
|
||||
|
||||
if (_tileEntities.Count == 0) {
|
||||
level["TileEntities"] = new NBT_List(NBT_Type.TAG_COMPOUND);
|
||||
_tileEntities = level["TileEntities"] as NBT_List;
|
||||
}
|
||||
|
||||
_cx = level["xPos"].ToNBTInt();
|
||||
_cz = level["zPos"].ToNBTInt();
|
||||
|
||||
BuildTileEntityCache();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Chunk LoadTreeSafe (NBT_Value tree)
|
||||
{
|
||||
if (!ValidateTree(tree)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return LoadTree(tree);
|
||||
}
|
||||
|
||||
public NBT_Value BuildTree ()
|
||||
{
|
||||
return _tree.Root;
|
||||
}
|
||||
|
||||
public bool ValidateTree (NBT_Value tree)
|
||||
{
|
||||
return new NBTVerifier(tree, LevelSchema).Verify();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
156
Substrate/SubstrateCS/Source/ChunkEnumerator.cs
Normal file
156
Substrate/SubstrateCS/Source/ChunkEnumerator.cs
Normal file
|
@ -0,0 +1,156 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
public class ChunkList : IEnumerable<ChunkRef>
|
||||
{
|
||||
//private List<Region> _regions;
|
||||
|
||||
protected ChunkManager _cm = null;
|
||||
protected Region _region = null;
|
||||
|
||||
// Constructor to enumerate a single region
|
||||
public ChunkList (ChunkManager cm, Region region)
|
||||
{
|
||||
_cm = cm;
|
||||
_region = region;
|
||||
}
|
||||
|
||||
// Constructor to enumerate all regions
|
||||
public ChunkList (ChunkManager cm)
|
||||
{
|
||||
_cm = cm;
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator ()
|
||||
{
|
||||
return (IEnumerator)GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator<ChunkRef> IEnumerable<ChunkRef>.GetEnumerator ()
|
||||
{
|
||||
return (IEnumerator<ChunkRef>)GetEnumerator();
|
||||
}
|
||||
|
||||
public virtual ChunkEnumerator GetEnumerator ()
|
||||
{
|
||||
return new ChunkEnumerator(_cm, _region);
|
||||
}
|
||||
}
|
||||
|
||||
public class ChunkEnumerator : IEnumerator<ChunkRef>
|
||||
{
|
||||
protected Region _region;
|
||||
protected ChunkManager _cm;
|
||||
|
||||
protected ChunkRef _chunk;
|
||||
|
||||
protected RegionEnumerator _enum = null;
|
||||
protected int _x = 0;
|
||||
protected int _z = -1;
|
||||
|
||||
public ChunkEnumerator (ChunkManager cm, Region region)
|
||||
{
|
||||
_cm = cm;
|
||||
_region = region;
|
||||
|
||||
if (_region == null) {
|
||||
_enum = new RegionEnumerator(_cm.GetRegionManager());
|
||||
_enum.MoveNext();
|
||||
_region = _enum.Current;
|
||||
}
|
||||
}
|
||||
|
||||
public ChunkEnumerator (ChunkManager cm)
|
||||
{
|
||||
_cm = cm;
|
||||
_enum = new RegionEnumerator(_cm.GetRegionManager());
|
||||
_enum.MoveNext();
|
||||
_region = _enum.Current;
|
||||
}
|
||||
|
||||
public virtual bool MoveNext ()
|
||||
{
|
||||
if (_enum == null) {
|
||||
return MoveNextInRegion();
|
||||
}
|
||||
else {
|
||||
while (true) {
|
||||
if (_x >= ChunkManager.REGION_XLEN) {
|
||||
if (!_enum.MoveNext()) {
|
||||
return false;
|
||||
}
|
||||
_x = 0;
|
||||
_z = -1;
|
||||
_region = _enum.Current;
|
||||
}
|
||||
if (MoveNextInRegion()) {
|
||||
_chunk = _cm.GetChunkRefInRegion(_region, _x, _z);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected bool MoveNextInRegion ()
|
||||
{
|
||||
for (; _x < ChunkManager.REGION_XLEN; _x++) {
|
||||
for (_z++; _z < ChunkManager.REGION_ZLEN; _z++) {
|
||||
if (_region.ChunkExists(_x, _z)) {
|
||||
goto FoundNext;
|
||||
}
|
||||
}
|
||||
_z = -1;
|
||||
}
|
||||
|
||||
FoundNext:
|
||||
|
||||
return (_x < ChunkManager.REGION_XLEN);
|
||||
}
|
||||
|
||||
public void Reset ()
|
||||
{
|
||||
if (_enum != null) {
|
||||
_enum.Reset();
|
||||
_enum.MoveNext();
|
||||
_region = _enum.Current;
|
||||
}
|
||||
_x = 0;
|
||||
_z = -1;
|
||||
}
|
||||
|
||||
void IDisposable.Dispose () { }
|
||||
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
|
||||
ChunkRef IEnumerator<ChunkRef>.Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
|
||||
public ChunkRef Current
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_x >= ChunkManager.REGION_XLEN) {
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
return _chunk;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
87
Substrate/SubstrateCS/Source/ChunkFile.cs
Normal file
87
Substrate/SubstrateCS/Source/ChunkFile.cs
Normal file
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using Ionic.Zlib;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
class ChunkFile
|
||||
{
|
||||
private string _filename;
|
||||
|
||||
public ChunkFile (string path)
|
||||
{
|
||||
_filename = path;
|
||||
}
|
||||
|
||||
public ChunkFile (string path, int cx, int cz)
|
||||
{
|
||||
string cx64 = Base64(cx);
|
||||
string cz64 = Base64(cz);
|
||||
string file = "c." + cx64 + "." + cz64 + ".dat";
|
||||
|
||||
string dir1 = Base64(cx % 64);
|
||||
string dir2 = Base64(cz % 64);
|
||||
|
||||
_filename = Path.Combine(path, dir1);
|
||||
_filename = Path.Combine(_filename, dir2);
|
||||
_filename = Path.Combine(_filename, file);
|
||||
}
|
||||
|
||||
private string Base64 (int val)
|
||||
{
|
||||
return Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(val.ToString()));
|
||||
}
|
||||
|
||||
public bool Exists ()
|
||||
{
|
||||
return File.Exists(_filename);
|
||||
}
|
||||
|
||||
public bool Delete ()
|
||||
{
|
||||
File.Delete(_filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
public Stream GetChunkDataInputStream ()
|
||||
{
|
||||
FileStream fstr = new FileStream(_filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||
|
||||
long length = fstr.Seek(0, SeekOrigin.End);
|
||||
fstr.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
byte[] data = new byte[length];
|
||||
fstr.Read(data, 0, data.Length);
|
||||
|
||||
fstr.Close();
|
||||
|
||||
return new GZipStream(new MemoryStream(data), CompressionMode.Decompress);
|
||||
}
|
||||
|
||||
public Stream GetChunkDataOutputStream ()
|
||||
{
|
||||
return new ZlibStream(new ChunkBuffer(this), CompressionMode.Compress);
|
||||
}
|
||||
|
||||
class ChunkBuffer : MemoryStream
|
||||
{
|
||||
private ChunkFile region;
|
||||
|
||||
public ChunkBuffer (ChunkFile c)
|
||||
: base(8096)
|
||||
{
|
||||
this.region = c;
|
||||
}
|
||||
|
||||
public override void Close ()
|
||||
{
|
||||
FileStream fstr = new FileStream(region._filename, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
|
||||
fstr.Write(this.GetBuffer(), 0, (int)this.Length);
|
||||
fstr.Close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
188
Substrate/SubstrateCS/Source/ChunkFileManager.cs
Normal file
188
Substrate/SubstrateCS/Source/ChunkFileManager.cs
Normal file
|
@ -0,0 +1,188 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
using NBT;
|
||||
|
||||
class ChunkFileManager : IChunkContainer, IChunkCache
|
||||
{
|
||||
protected string _mapPath;
|
||||
|
||||
protected Dictionary<ChunkKey, WeakReference> _cache;
|
||||
protected Dictionary<ChunkKey, ChunkRef> _dirty;
|
||||
|
||||
public ChunkFileManager (string mapDir)
|
||||
{
|
||||
_mapPath = mapDir;
|
||||
_cache = new Dictionary<ChunkKey, WeakReference>();
|
||||
_dirty = new Dictionary<ChunkKey, ChunkRef>();
|
||||
}
|
||||
|
||||
protected ChunkFile GetChunkFile (int cx, int cz)
|
||||
{
|
||||
return new ChunkFile(_mapPath, cx, cz);
|
||||
}
|
||||
|
||||
protected NBT_Tree GetChunkTree (int cx, int cz)
|
||||
{
|
||||
ChunkFile cf = GetChunkFile(cx, cz);
|
||||
Stream nbtstr = cf.GetChunkDataInputStream();
|
||||
if (nbtstr == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new NBT_Tree(nbtstr);
|
||||
}
|
||||
|
||||
protected bool SaveChunkTree (int cx, int cz, NBT_Tree tree)
|
||||
{
|
||||
ChunkFile cf = GetChunkFile(cx, cz);
|
||||
Stream zipstr = cf.GetChunkDataOutputStream();
|
||||
if (zipstr == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tree.WriteTo(zipstr);
|
||||
zipstr.Close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected Stream GetChunkOutStream (int cx, int cz)
|
||||
{
|
||||
return new ChunkFile(_mapPath, cx, cz).GetChunkDataOutputStream();
|
||||
}
|
||||
|
||||
#region IChunkContainer Members
|
||||
|
||||
public int ChunkGlobalX (int cx)
|
||||
{
|
||||
return cx;
|
||||
}
|
||||
|
||||
public int ChunkGlobalZ (int cz)
|
||||
{
|
||||
return cz;
|
||||
}
|
||||
|
||||
public int ChunkLocalX (int cx)
|
||||
{
|
||||
return cx;
|
||||
}
|
||||
|
||||
public int ChunkLocalZ (int cz)
|
||||
{
|
||||
return cz;
|
||||
}
|
||||
|
||||
public Chunk GetChunk (int cx, int cz)
|
||||
{
|
||||
if (!ChunkExists(cx, cz)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Chunk(GetChunkTree(cx, cz));
|
||||
}
|
||||
|
||||
public ChunkRef GetChunkRef (int cx, int cz)
|
||||
{
|
||||
ChunkKey k = new ChunkKey(cx, cz);
|
||||
|
||||
ChunkRef c = null;
|
||||
|
||||
WeakReference chunkref = null;
|
||||
if (_cache.TryGetValue(k, out chunkref)) {
|
||||
c = chunkref.Target as ChunkRef;
|
||||
}
|
||||
else {
|
||||
_cache.Add(k, new WeakReference(null));
|
||||
}
|
||||
|
||||
if (c != null) {
|
||||
return c;
|
||||
}
|
||||
|
||||
try {
|
||||
c = new ChunkRef(this, this, cx, cz);
|
||||
_cache[k].Target = c;
|
||||
return c;
|
||||
}
|
||||
catch (MissingChunkException) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ChunkExists (int cx, int cz)
|
||||
{
|
||||
return new ChunkFile(_mapPath, cx, cz).Exists();
|
||||
}
|
||||
|
||||
public bool DeleteChunk (int cx, int cz)
|
||||
{
|
||||
new ChunkFile(_mapPath, cx, cz).Delete();
|
||||
|
||||
ChunkKey k = new ChunkKey(cx, cz);
|
||||
_cache.Remove(k);
|
||||
_dirty.Remove(k);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int Save ()
|
||||
{
|
||||
int saved = 0;
|
||||
foreach (ChunkRef c in _dirty.Values) {
|
||||
int cx = ChunkGlobalX(c.X);
|
||||
int cz = ChunkGlobalZ(c.Z);
|
||||
|
||||
if (c.Save(GetChunkOutStream(cx, cz))) {
|
||||
saved++;
|
||||
}
|
||||
}
|
||||
|
||||
_dirty.Clear();
|
||||
return saved;
|
||||
}
|
||||
|
||||
public bool SaveChunk (Chunk chunk)
|
||||
{
|
||||
return chunk.Save(GetChunkOutStream(ChunkGlobalX(chunk.X), ChunkGlobalZ(chunk.Z)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IChunkCache Members
|
||||
|
||||
public bool MarkChunkDirty (ChunkRef chunk)
|
||||
{
|
||||
int cx = chunk.X;
|
||||
int cz = chunk.Z;
|
||||
|
||||
ChunkKey k = new ChunkKey(cx, cz);
|
||||
if (!_dirty.ContainsKey(k)) {
|
||||
_dirty.Add(k, GetChunkRef(cx, cz));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool MarkChunkClean (ChunkRef chunk)
|
||||
{
|
||||
int cx = chunk.X;
|
||||
int cz = chunk.Z;
|
||||
|
||||
ChunkKey k = new ChunkKey(cx, cz);
|
||||
if (_dirty.ContainsKey(k)) {
|
||||
_dirty.Remove(k);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
48
Substrate/SubstrateCS/Source/ChunkInterface.cs
Normal file
48
Substrate/SubstrateCS/Source/ChunkInterface.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
|
||||
public interface IChunk : IBlockContainer
|
||||
{
|
||||
int X { get; }
|
||||
int Z { get; }
|
||||
|
||||
bool IsTerrainPopulated { get; set; }
|
||||
|
||||
bool Save (Stream outStream);
|
||||
|
||||
int CountBlockID (int id);
|
||||
int CountBlockData (int id, int data);
|
||||
|
||||
int GetHeight (int lx, int lz);
|
||||
}
|
||||
|
||||
public interface IChunkCache
|
||||
{
|
||||
bool MarkChunkDirty (ChunkRef chunk);
|
||||
bool MarkChunkClean (ChunkRef chunk);
|
||||
}
|
||||
|
||||
public interface IChunkContainer
|
||||
{
|
||||
int ChunkGlobalX (int cx);
|
||||
int ChunkGlobalZ (int cz);
|
||||
|
||||
int ChunkLocalX (int cx);
|
||||
int ChunkLocalZ (int cz);
|
||||
|
||||
Chunk GetChunk (int cx, int cz);
|
||||
ChunkRef GetChunkRef (int cx, int cz);
|
||||
|
||||
bool ChunkExists (int cx, int cz);
|
||||
|
||||
bool DeleteChunk (int cx, int cz);
|
||||
|
||||
int Save ();
|
||||
bool SaveChunk (Chunk chunk);
|
||||
}
|
||||
}
|
51
Substrate/SubstrateCS/Source/ChunkKey.cs
Normal file
51
Substrate/SubstrateCS/Source/ChunkKey.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
public struct ChunkKey : IEquatable<ChunkKey>
|
||||
{
|
||||
readonly int cx;
|
||||
readonly int cz;
|
||||
|
||||
public ChunkKey (int _cx, int _cz)
|
||||
{
|
||||
cx = _cx;
|
||||
cz = _cz;
|
||||
}
|
||||
|
||||
public bool Equals (ChunkKey ck)
|
||||
{
|
||||
return this.cx == ck.cx && this.cz == ck.cz;
|
||||
}
|
||||
|
||||
public override bool Equals (Object o)
|
||||
{
|
||||
try {
|
||||
return this == (ChunkKey)o;
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode ()
|
||||
{
|
||||
int hash = 23;
|
||||
hash = hash * 37 + cx;
|
||||
hash = hash * 37 + cz;
|
||||
return hash;
|
||||
}
|
||||
|
||||
public static bool operator == (ChunkKey k1, ChunkKey k2)
|
||||
{
|
||||
return k1.cx == k2.cx && k1.cz == k2.cz;
|
||||
}
|
||||
|
||||
public static bool operator != (ChunkKey k1, ChunkKey k2)
|
||||
{
|
||||
return k1.cx != k2.cx || k1.cz != k2.cz;
|
||||
}
|
||||
}
|
||||
}
|
196
Substrate/SubstrateCS/Source/ChunkManager.cs
Normal file
196
Substrate/SubstrateCS/Source/ChunkManager.cs
Normal file
|
@ -0,0 +1,196 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
|
||||
public class ChunkManager : IChunkContainer, IChunkCache, IEnumerable<ChunkRef>
|
||||
{
|
||||
public const int REGION_XLEN = 32;
|
||||
public const int REGION_ZLEN = 32;
|
||||
|
||||
public const int REGION_XLOG = 5;
|
||||
public const int REGION_ZLOG = 5;
|
||||
|
||||
public const int REGION_XMASK = 0x1F;
|
||||
public const int REGION_ZMASK = 0x1F;
|
||||
|
||||
protected RegionManager _regionMan;
|
||||
|
||||
protected Dictionary<RegionKey, Region> _cache;
|
||||
protected Dictionary<RegionKey, Region> _dirty;
|
||||
|
||||
public ChunkManager (RegionManager rm)
|
||||
{
|
||||
_regionMan = rm;
|
||||
_cache = new Dictionary<RegionKey, Region>();
|
||||
_dirty = new Dictionary<RegionKey, Region>();
|
||||
}
|
||||
|
||||
public int ChunkGlobalX (int cx)
|
||||
{
|
||||
return cx;
|
||||
}
|
||||
|
||||
public int ChunkGlobalZ (int cz)
|
||||
{
|
||||
return cz;
|
||||
}
|
||||
|
||||
public int ChunkLocalX (int cx)
|
||||
{
|
||||
return cx & REGION_XMASK;
|
||||
}
|
||||
|
||||
public int ChunkLocalZ (int cz)
|
||||
{
|
||||
return cz & REGION_ZMASK;
|
||||
}
|
||||
|
||||
public Chunk GetChunk (int cx, int cz)
|
||||
{
|
||||
Region r = GetRegion(cx, cz);
|
||||
if (r == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return r.GetChunk(cx & REGION_XMASK, cz & REGION_ZMASK);
|
||||
}
|
||||
|
||||
public ChunkRef GetChunkRef (int cx, int cz)
|
||||
{
|
||||
Region r = GetRegion(cx, cz);
|
||||
if (r == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return r.GetChunkRef(cx & REGION_XMASK, cz & REGION_ZMASK, this);
|
||||
}
|
||||
|
||||
public bool ChunkExists (int cx, int cz)
|
||||
{
|
||||
Region r = GetRegion(cx, cz);
|
||||
if (r == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return r.ChunkExists(cx & REGION_XMASK, cz & REGION_ZMASK);
|
||||
}
|
||||
|
||||
public bool MarkChunkDirty (ChunkRef chunk)
|
||||
{
|
||||
Region r = GetRegion(chunk.X, chunk.Z);
|
||||
if (r == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RegionKey k = new RegionKey(r.X, r.Z);
|
||||
_dirty[k] = r;
|
||||
|
||||
r.MarkChunkDirty(chunk);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool MarkChunkClean (ChunkRef chunk)
|
||||
{
|
||||
Region r = GetRegion(chunk.X, chunk.Z);
|
||||
if (r == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RegionKey k = new RegionKey(r.X, r.Z);
|
||||
_dirty.Remove(k);
|
||||
|
||||
r.MarkChunkClean(chunk);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int Save ()
|
||||
{
|
||||
int saved = 0;
|
||||
foreach (Region r in _dirty.Values) {
|
||||
saved += r.Save();
|
||||
}
|
||||
|
||||
_dirty.Clear();
|
||||
return saved;
|
||||
}
|
||||
|
||||
public bool SaveChunk (Chunk chunk)
|
||||
{
|
||||
Region r = GetRegion(chunk.X, chunk.Z);
|
||||
if (r == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return r.SaveChunk(chunk);
|
||||
}
|
||||
|
||||
public bool DeleteChunk (int cx, int cz)
|
||||
{
|
||||
Region r = GetRegion(cx, cz);
|
||||
if (r == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!r.DeleteChunk(cx & REGION_XMASK, cz & REGION_ZMASK)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (r.ChunkCount() == 0) {
|
||||
RegionKey k = new RegionKey(r.X, r.Z);
|
||||
_cache.Remove(k);
|
||||
_dirty.Remove(k);
|
||||
|
||||
_regionMan.DeleteRegion(r.X, r.Z);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public RegionManager GetRegionManager ()
|
||||
{
|
||||
return _regionMan;
|
||||
}
|
||||
|
||||
public ChunkRef GetChunkRefInRegion (Region r, int lcx, int lcz)
|
||||
{
|
||||
int cx = r.X * REGION_XLEN + lcx;
|
||||
int cz = r.Z * REGION_ZLEN + lcz;
|
||||
return GetChunkRef(cx, cz);
|
||||
}
|
||||
|
||||
protected Region GetRegion (int cx, int cz)
|
||||
{
|
||||
cx >>= REGION_XLOG;
|
||||
cz >>= REGION_ZLOG;
|
||||
return _regionMan.GetRegion(cx, cz);
|
||||
}
|
||||
|
||||
#region IEnumerable<ChunkRef> Members
|
||||
|
||||
public IEnumerator<ChunkRef> GetEnumerator ()
|
||||
{
|
||||
return new ChunkEnumerator(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
|
||||
{
|
||||
return new ChunkEnumerator(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class MissingChunkException : Exception
|
||||
{
|
||||
|
||||
}
|
||||
}
|
328
Substrate/SubstrateCS/Source/ChunkRef.cs
Normal file
328
Substrate/SubstrateCS/Source/ChunkRef.cs
Normal file
|
@ -0,0 +1,328 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
using NBT;
|
||||
|
||||
public class ChunkRef : IChunk
|
||||
{
|
||||
private IChunkContainer _container;
|
||||
private IChunkCache _cache;
|
||||
private Chunk _chunk;
|
||||
|
||||
private int _cx;
|
||||
private int _cz;
|
||||
|
||||
private bool _dirty;
|
||||
|
||||
public int X
|
||||
{
|
||||
get { return _container.ChunkGlobalX(_cx); }
|
||||
}
|
||||
|
||||
public int Z
|
||||
{
|
||||
get { return _container.ChunkGlobalZ(_cz); }
|
||||
}
|
||||
|
||||
public int LocalX
|
||||
{
|
||||
get { return _container.ChunkLocalX(_cx); }
|
||||
}
|
||||
|
||||
public int LocalZ
|
||||
{
|
||||
get { return _container.ChunkLocalZ(_cz); }
|
||||
}
|
||||
|
||||
public ChunkRef (IChunkContainer container, IChunkCache cache, int cx, int cz)
|
||||
{
|
||||
_container = container;
|
||||
_cache = cache;
|
||||
_cx = cx;
|
||||
_cz = cz;
|
||||
|
||||
if (!_container.ChunkExists(cx, cz)) {
|
||||
throw new MissingChunkException();
|
||||
}
|
||||
}
|
||||
|
||||
public int BlockGlobalX (int x)
|
||||
{
|
||||
return _container.ChunkGlobalX(_cx) * BlockManager.CHUNK_XLEN + x;
|
||||
}
|
||||
|
||||
public int BlockGlobalY (int y)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
public int BlockGlobalZ (int z)
|
||||
{
|
||||
return _container.ChunkGlobalZ(_cz) * BlockManager.CHUNK_ZLEN + z;
|
||||
}
|
||||
|
||||
public int BlockLocalX (int x)
|
||||
{
|
||||
return x;
|
||||
}
|
||||
|
||||
public int BlockLocalY (int y)
|
||||
{
|
||||
return y;
|
||||
}
|
||||
|
||||
public int BlockLocalZ (int z)
|
||||
{
|
||||
return z;
|
||||
}
|
||||
|
||||
private Chunk GetChunk ()
|
||||
{
|
||||
if (_chunk == null) {
|
||||
_chunk = _container.GetChunk(_cx, _cz);
|
||||
}
|
||||
return _chunk;
|
||||
}
|
||||
|
||||
private bool MarkDirty ()
|
||||
{
|
||||
if (_dirty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
_dirty = true;
|
||||
_cache.MarkChunkDirty(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public ChunkRef GetNorthNeighbor ()
|
||||
{
|
||||
return _container.GetChunkRef(_cx - 1, _cz);
|
||||
}
|
||||
|
||||
public ChunkRef GetSouthNeighbor ()
|
||||
{
|
||||
return _container.GetChunkRef(_cx + 1, _cz);
|
||||
}
|
||||
|
||||
public ChunkRef GetEastNeighbor ()
|
||||
{
|
||||
return _container.GetChunkRef(_cx, _cz - 1);
|
||||
}
|
||||
|
||||
public ChunkRef GetWestNeighbor ()
|
||||
{
|
||||
return _container.GetChunkRef(_cx, _cz + 1);
|
||||
}
|
||||
|
||||
public Chunk GetChunkCopy ()
|
||||
{
|
||||
return GetChunk().Copy();
|
||||
}
|
||||
|
||||
public Chunk GetChunkRef ()
|
||||
{
|
||||
Chunk chunk = GetChunk();
|
||||
_chunk = null;
|
||||
|
||||
return chunk;
|
||||
}
|
||||
|
||||
public void SetChunkRef (Chunk chunk)
|
||||
{
|
||||
_chunk = chunk;
|
||||
_chunk.SetLocation(_cx, _cz);
|
||||
MarkDirty();
|
||||
}
|
||||
|
||||
#region IChunk Members
|
||||
|
||||
public bool IsTerrainPopulated
|
||||
{
|
||||
get { return GetChunk().IsTerrainPopulated; }
|
||||
set
|
||||
{
|
||||
if (GetChunk().IsTerrainPopulated != value) {
|
||||
GetChunk().IsTerrainPopulated = value;
|
||||
MarkDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool Save (Stream outStream)
|
||||
{
|
||||
if (_dirty) {
|
||||
if (GetChunk().Save(outStream)) {
|
||||
_dirty = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Block GetBlock (int lx, int ly, int lz)
|
||||
{
|
||||
return new Block(this, lx, ly, lz);
|
||||
}
|
||||
|
||||
public BlockRef GetBlockRef (int lx, int ly, int lz)
|
||||
{
|
||||
return new BlockRef(this, lx, ly, lz);
|
||||
}
|
||||
|
||||
public BlockInfo GetBlockInfo (int lx, int ly, int lz)
|
||||
{
|
||||
return GetChunk().GetBlockInfo(lx, ly, lz);
|
||||
}
|
||||
|
||||
public void SetBlock (int lx, int ly, int lz, Block block)
|
||||
{
|
||||
GetChunk().SetBlock(lx, ly, lz, block);
|
||||
}
|
||||
|
||||
public int GetBlockID (int lx, int ly, int lz)
|
||||
{
|
||||
return GetChunk().GetBlockID(lx, ly, lz);
|
||||
}
|
||||
|
||||
public int GetBlockData (int lx, int ly, int lz)
|
||||
{
|
||||
return GetChunk().GetBlockData(lx, ly, lz);
|
||||
}
|
||||
|
||||
public int GetBlockLight (int lx, int ly, int lz)
|
||||
{
|
||||
return GetChunk().GetBlockSkyLight(lx, ly, lz);
|
||||
}
|
||||
|
||||
public int GetBlockSkyLight (int lx, int ly, int lz)
|
||||
{
|
||||
return GetChunk().GetBlockSkyLight(lx, ly, lz);
|
||||
}
|
||||
|
||||
public bool SetBlockID (int lx, int ly, int lz, int id)
|
||||
{
|
||||
if (GetChunk().SetBlockID(lx, ly, lz, id)) {
|
||||
MarkDirty();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool SetBlockData (int lx, int ly, int lz, int data)
|
||||
{
|
||||
if (GetChunk().SetBlockData(lx, ly, lz, data)) {
|
||||
MarkDirty();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool SetBlockLight (int lx, int ly, int lz, int light)
|
||||
{
|
||||
if (GetChunk().SetBlockLight(lx, ly, lz, light)) {
|
||||
MarkDirty();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool SetBlockSkyLight (int lx, int ly, int lz, int light)
|
||||
{
|
||||
if (GetChunk().SetBlockSkyLight(lx, ly, lz, light)) {
|
||||
MarkDirty();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int CountBlockID (int id)
|
||||
{
|
||||
return GetChunk().CountBlockID(id);
|
||||
}
|
||||
|
||||
public int CountBlockData (int id, int data)
|
||||
{
|
||||
return GetChunk().CountBlockData(id, data);
|
||||
}
|
||||
|
||||
public int GetHeight (int lx, int lz)
|
||||
{
|
||||
return GetChunk().GetHeight(lx, lz);
|
||||
}
|
||||
|
||||
public TileEntity GetTileEntity (int lx, int ly, int lz)
|
||||
{
|
||||
return GetChunk().GetTileEntity(lx, ly, lz);
|
||||
}
|
||||
|
||||
public bool SetTileEntity (int lx, int ly, int lz, TileEntity te)
|
||||
{
|
||||
if (GetChunk().SetTileEntity(lx, ly, lz, te)) {
|
||||
MarkDirty();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool ClearTileEntity (int lx, int ly, int lz)
|
||||
{
|
||||
if (GetChunk().ClearTileEntity(lx, ly, lz)) {
|
||||
MarkDirty();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/*public bool VerifyTileEntities ()
|
||||
{
|
||||
bool pass = true;
|
||||
|
||||
NBT_List telist = GetTree().Root["Level"].ToNBTCompound()["TileEntities"].ToNBTList();
|
||||
|
||||
foreach (NBT_Value val in telist) {
|
||||
NBT_Compound tree = val as NBT_Compound;
|
||||
if (tree == null) {
|
||||
pass = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (new NBTVerifier(tree, TileEntity.BaseSchema).Verify() == false) {
|
||||
pass = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
int x = tree["x"].ToNBTInt() & BlockManager.CHUNK_XMASK;
|
||||
int y = tree["y"].ToNBTInt() & BlockManager.CHUNK_YMASK;
|
||||
int z = tree["z"].ToNBTInt() & BlockManager.CHUNK_ZMASK;
|
||||
int id = GetBlockID(x, y, z);
|
||||
|
||||
NBTCompoundNode schema = BlockInfo.SchemaTable[id];
|
||||
if (schema == null) {
|
||||
pass = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
pass = new NBTVerifier(tree, schema).Verify() && pass;
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
private static bool LocalBounds (int lx, int ly, int lz)
|
||||
{
|
||||
return lx >= 0 && lx < BlockManager.CHUNK_XLEN &&
|
||||
ly >= 0 && ly < BlockManager.CHUNK_YLEN &&
|
||||
lz >= 0 && lz < BlockManager.CHUNK_ZLEN;
|
||||
}*/
|
||||
|
||||
public class MalformedNBTTreeException : Exception { }
|
||||
}
|
134
Substrate/SubstrateCS/Source/Entity.cs
Normal file
134
Substrate/SubstrateCS/Source/Entity.cs
Normal file
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
using NBT;
|
||||
using Utility;
|
||||
|
||||
public class Entity
|
||||
{
|
||||
private string _entityID;
|
||||
private double _posX;
|
||||
private double _posY;
|
||||
private double _posZ;
|
||||
private double _motionX;
|
||||
private double _motionY;
|
||||
private double _motionZ;
|
||||
private float _rotationYaw;
|
||||
private float _rotationPitch;
|
||||
private float _fallDistance;
|
||||
private short _fire;
|
||||
private short _air;
|
||||
private byte _onGround;
|
||||
|
||||
|
||||
#region Predefined Schemas
|
||||
|
||||
public static readonly NBTCompoundNode BaseSchema = new NBTCompoundNode("")
|
||||
{
|
||||
new NBTScalerNode("id", NBT_Type.TAG_STRING),
|
||||
new NBTListNode("Pos", NBT_Type.TAG_DOUBLE, 3),
|
||||
new NBTListNode("Motion", NBT_Type.TAG_DOUBLE, 3),
|
||||
new NBTListNode("Rotation", NBT_Type.TAG_FLOAT, 2),
|
||||
new NBTScalerNode("FallDistance", NBT_Type.TAG_FLOAT),
|
||||
new NBTScalerNode("Fire", NBT_Type.TAG_SHORT),
|
||||
new NBTScalerNode("Air", NBT_Type.TAG_SHORT),
|
||||
new NBTScalerNode("OnGround", NBT_Type.TAG_BYTE),
|
||||
};
|
||||
|
||||
public static readonly NBTCompoundNode MobSchema = BaseSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Mob"),
|
||||
new NBTScalerNode("AttackTime", NBT_Type.TAG_SHORT),
|
||||
new NBTScalerNode("DeathTime", NBT_Type.TAG_SHORT),
|
||||
new NBTScalerNode("Health", NBT_Type.TAG_SHORT),
|
||||
new NBTScalerNode("HurtTime", NBT_Type.TAG_SHORT),
|
||||
});
|
||||
|
||||
public static readonly NBTCompoundNode MonsterSchema = MobSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Monster"),
|
||||
});
|
||||
|
||||
public static readonly NBTCompoundNode CreeperSchema = MobSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Creeper"),
|
||||
});
|
||||
|
||||
public static readonly NBTCompoundNode SkeletonSchema = MobSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Skeleton"),
|
||||
});
|
||||
|
||||
public static readonly NBTCompoundNode SpiderSchema = MobSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Spider"),
|
||||
});
|
||||
|
||||
public static readonly NBTCompoundNode GiantSchema = MobSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Giant"),
|
||||
});
|
||||
|
||||
public static readonly NBTCompoundNode ZombieSchema = MobSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Zombie"),
|
||||
});
|
||||
|
||||
public static readonly NBTCompoundNode PigZombieSchema = MobSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "PigZombie"),
|
||||
});
|
||||
|
||||
public static readonly NBTCompoundNode GhastSchema = MobSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Ghast"),
|
||||
});
|
||||
|
||||
public static readonly NBTCompoundNode PigSchema = MobSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Pig"),
|
||||
new NBTScalerNode("Saddle", NBT_Type.TAG_BYTE),
|
||||
});
|
||||
|
||||
public static readonly NBTCompoundNode SheepSchema = MobSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Sheep"),
|
||||
new NBTScalerNode("Sheared", NBT_Type.TAG_BYTE),
|
||||
new NBTScalerNode("Color", NBT_Type.TAG_BYTE),
|
||||
});
|
||||
|
||||
public static readonly NBTCompoundNode CowSchema = MobSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Cow"),
|
||||
});
|
||||
|
||||
public static readonly NBTCompoundNode ChickenSchema = MobSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Chicken"),
|
||||
});
|
||||
|
||||
public static readonly NBTCompoundNode Slimechema = MobSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Slime"),
|
||||
new NBTScalerNode("Size", NBT_Type.TAG_INT),
|
||||
});
|
||||
|
||||
public static readonly NBTCompoundNode WolfSchema = MobSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Wolf"),
|
||||
new NBTScalerNode("Owner", NBT_Type.TAG_STRING),
|
||||
new NBTScalerNode("Sitting", NBT_Type.TAG_BYTE),
|
||||
new NBTScalerNode("Angry", NBT_Type.TAG_BYTE),
|
||||
});
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class MobEntity : Entity
|
||||
{
|
||||
|
||||
}
|
||||
}
|
227
Substrate/SubstrateCS/Source/Item.cs
Normal file
227
Substrate/SubstrateCS/Source/Item.cs
Normal file
|
@ -0,0 +1,227 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
using NBT;
|
||||
using Utility;
|
||||
|
||||
public interface IItemContainer
|
||||
{
|
||||
ItemCollection Items { get; }
|
||||
}
|
||||
|
||||
public class Item : INBTObject<Item>, ICopyable<Item>
|
||||
{
|
||||
public static readonly NBTCompoundNode ItemSchema = new NBTCompoundNode("")
|
||||
{
|
||||
new NBTScalerNode("id", NBT_Type.TAG_SHORT),
|
||||
new NBTScalerNode("Damage", NBT_Type.TAG_SHORT),
|
||||
new NBTScalerNode("Count", NBT_Type.TAG_BYTE),
|
||||
};
|
||||
|
||||
private short _id;
|
||||
private byte _count;
|
||||
private short _damage;
|
||||
|
||||
public int ID
|
||||
{
|
||||
get { return _id; }
|
||||
set { _id = (short)value; }
|
||||
}
|
||||
|
||||
public int Damage
|
||||
{
|
||||
get { return _damage; }
|
||||
set { _damage = (short)value; }
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return _count; }
|
||||
set { _count = (byte)value; }
|
||||
}
|
||||
|
||||
public Item ()
|
||||
{
|
||||
}
|
||||
|
||||
#region ICopyable<Item> Members
|
||||
|
||||
public Item Copy ()
|
||||
{
|
||||
Item item = new Item();
|
||||
item._id = _id;
|
||||
item._count = _count;
|
||||
item._damage = _damage;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INBTObject<Item> Members
|
||||
|
||||
public Item LoadTree (NBT_Value tree)
|
||||
{
|
||||
NBT_Compound ctree = tree as NBT_Compound;
|
||||
if (ctree == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_id = ctree["id"].ToNBTShort();
|
||||
_count = ctree["Count"].ToNBTByte();
|
||||
_damage = ctree["Damage"].ToNBTShort();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Item LoadTreeSafe (NBT_Value tree)
|
||||
{
|
||||
if (!ValidateTree(tree)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return LoadTree(tree);
|
||||
}
|
||||
|
||||
public NBT_Value BuildTree ()
|
||||
{
|
||||
NBT_Compound tree = new NBT_Compound();
|
||||
tree["id"] = new NBT_Short(_id);
|
||||
tree["Count"] = new NBT_Byte(_count);
|
||||
tree["Damage"] = new NBT_Short(_damage);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
public bool ValidateTree (NBT_Value tree)
|
||||
{
|
||||
return new NBTVerifier(tree, ItemSchema).Verify();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class ItemCollection : INBTObject<ItemCollection>, ICopyable<ItemCollection>
|
||||
{
|
||||
public static readonly NBTCompoundNode InventorySchema = Item.ItemSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTScalerNode("Slot", NBT_Type.TAG_BYTE),
|
||||
});
|
||||
|
||||
public static readonly NBTListNode ListSchema = new NBTListNode("", NBT_Type.TAG_COMPOUND, InventorySchema);
|
||||
|
||||
protected Dictionary<int, Item> _items;
|
||||
protected int _capacity;
|
||||
|
||||
public ItemCollection (int capacity)
|
||||
{
|
||||
_capacity = capacity;
|
||||
_items = new Dictionary<int, Item>();
|
||||
}
|
||||
|
||||
public int Capacity
|
||||
{
|
||||
get { return _capacity; }
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return _items.Count; }
|
||||
}
|
||||
|
||||
public Item this [int slot]
|
||||
{
|
||||
get
|
||||
{
|
||||
Item item;
|
||||
_items.TryGetValue(slot, out item);
|
||||
return item;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (slot < 0 || slot >= _capacity) {
|
||||
return;
|
||||
}
|
||||
_items[slot] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ItemExists (int slot)
|
||||
{
|
||||
return _items.ContainsKey(slot);
|
||||
}
|
||||
|
||||
public bool Clear (int slot)
|
||||
{
|
||||
return _items.Remove(slot);
|
||||
}
|
||||
|
||||
public void ClearAllItems ()
|
||||
{
|
||||
_items.Clear();
|
||||
}
|
||||
|
||||
#region ICopyable<ItemCollection> Members
|
||||
|
||||
public ItemCollection Copy ()
|
||||
{
|
||||
ItemCollection ic = new ItemCollection(_capacity);
|
||||
foreach (KeyValuePair<int, Item> item in _items) {
|
||||
ic[item.Key] = item.Value.Copy();
|
||||
}
|
||||
return ic;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INBTObject<ItemCollection> Members
|
||||
|
||||
public ItemCollection LoadTree (NBT_Value tree)
|
||||
{
|
||||
NBT_List ltree = tree as NBT_List;
|
||||
if (ltree == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach (NBT_Compound item in ltree) {
|
||||
int slot = item["Slot"].ToNBTByte();
|
||||
_items[slot] = new Item().LoadTree(item);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemCollection LoadTreeSafe (NBT_Value tree)
|
||||
{
|
||||
if (!ValidateTree(tree)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return LoadTree(tree);
|
||||
}
|
||||
|
||||
public NBT_Value BuildTree ()
|
||||
{
|
||||
NBT_List list = new NBT_List(NBT_Type.TAG_COMPOUND);
|
||||
|
||||
foreach (KeyValuePair<int, Item> item in _items) {
|
||||
NBT_Compound itemtree = item.Value.BuildTree() as NBT_Compound;
|
||||
itemtree["Slot"] = new NBT_Byte((byte)item.Key);
|
||||
list.Add(itemtree);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public bool ValidateTree (NBT_Value tree)
|
||||
{
|
||||
return new NBTVerifier(tree, ListSchema).Verify();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
167
Substrate/SubstrateCS/Source/NBT/JSONSerializer.cs
Normal file
167
Substrate/SubstrateCS/Source/NBT/JSONSerializer.cs
Normal file
|
@ -0,0 +1,167 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map.NBT
|
||||
{
|
||||
class JSONSerializer
|
||||
{
|
||||
public static string Serialize (NBT_Value tag)
|
||||
{
|
||||
return Serialize(tag, 0);
|
||||
}
|
||||
|
||||
public static string Serialize (NBT_Value tag, int level)
|
||||
{
|
||||
StringBuilder str = new StringBuilder();
|
||||
|
||||
if (tag.GetNBTType() == NBT_Type.TAG_COMPOUND) {
|
||||
SerializeCompound(tag as NBT_Compound, str, level);
|
||||
}
|
||||
else if (tag.GetNBTType() == NBT_Type.TAG_LIST) {
|
||||
SerializeList(tag as NBT_List, str, level);
|
||||
}
|
||||
else {
|
||||
SerializeScaler(tag, str);
|
||||
}
|
||||
|
||||
return str.ToString();
|
||||
}
|
||||
|
||||
private static void SerializeCompound (NBT_Compound tag, StringBuilder str, int level)
|
||||
{
|
||||
if (tag.Count == 0) {
|
||||
str.Append("{ }");
|
||||
return;
|
||||
}
|
||||
|
||||
str.AppendLine();
|
||||
AddLine(str, "{", level);
|
||||
|
||||
IEnumerator<KeyValuePair<string, NBT_Value>> en = tag.GetEnumerator();
|
||||
bool first = true;
|
||||
while (en.MoveNext()) {
|
||||
if (!first) {
|
||||
str.Append(",");
|
||||
str.AppendLine();
|
||||
}
|
||||
|
||||
KeyValuePair<string, NBT_Value> item = en.Current;
|
||||
Add(str, "\"" + item.Key + "\": ", level + 1);
|
||||
|
||||
if (item.Value.GetNBTType() == NBT_Type.TAG_COMPOUND) {
|
||||
SerializeCompound(item.Value as NBT_Compound, str, level + 1);
|
||||
}
|
||||
else if (item.Value.GetNBTType() == NBT_Type.TAG_LIST) {
|
||||
SerializeList(item.Value as NBT_List, str, level + 1);
|
||||
}
|
||||
else {
|
||||
SerializeScaler(item.Value, str);
|
||||
}
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
str.AppendLine();
|
||||
Add(str, "}", level);
|
||||
}
|
||||
|
||||
private static void SerializeList (NBT_List tag, StringBuilder str, int level)
|
||||
{
|
||||
if (tag.Count == 0) {
|
||||
str.Append("[ ]");
|
||||
return;
|
||||
}
|
||||
|
||||
str.AppendLine();
|
||||
AddLine(str, "[", level);
|
||||
|
||||
IEnumerator<NBT_Value> en = tag.GetEnumerator();
|
||||
bool first = true;
|
||||
while (en.MoveNext()) {
|
||||
if (!first) {
|
||||
str.Append(",");
|
||||
}
|
||||
|
||||
NBT_Value item = en.Current;
|
||||
|
||||
if (item.GetNBTType() == NBT_Type.TAG_COMPOUND) {
|
||||
SerializeCompound(item as NBT_Compound, str, level + 1);
|
||||
}
|
||||
else if (item.GetNBTType() == NBT_Type.TAG_LIST) {
|
||||
SerializeList(item as NBT_List, str, level + 1);
|
||||
}
|
||||
else {
|
||||
if (!first) {
|
||||
str.AppendLine();
|
||||
}
|
||||
Indent(str, level + 1);
|
||||
SerializeScaler(item, str);
|
||||
}
|
||||
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
str.AppendLine();
|
||||
Add(str, "]", level);
|
||||
}
|
||||
|
||||
private static void SerializeScaler (NBT_Value tag, StringBuilder str)
|
||||
{
|
||||
NBT_Type type = tag.GetNBTType();
|
||||
switch (tag.GetNBTType()) {
|
||||
case NBT_Type.TAG_STRING:
|
||||
str.Append("\"" + tag.ToNBTString().Data + "\"");
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_BYTE:
|
||||
str.Append(tag.ToNBTByte().Data);
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_SHORT:
|
||||
str.Append(tag.ToNBTShort().Data);
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_INT:
|
||||
str.Append(tag.ToNBTInt().Data);
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_LONG:
|
||||
str.Append(tag.ToNBTLong().Data);
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_FLOAT:
|
||||
str.Append(tag.ToNBTFloat().Data);
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_DOUBLE:
|
||||
str.Append(tag.ToNBTDouble().Data);
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_BYTE_ARRAY:
|
||||
str.Append(Convert.ToBase64String(tag.ToNBTByteArray().Data));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddLine (StringBuilder str, string line, int level)
|
||||
{
|
||||
Indent(str, level);
|
||||
str.AppendLine(line);
|
||||
}
|
||||
|
||||
private static void Add (StringBuilder str, string line, int level)
|
||||
{
|
||||
Indent(str, level);
|
||||
str.Append(line);
|
||||
}
|
||||
|
||||
private static void Indent (StringBuilder str, int count)
|
||||
{
|
||||
for (int i = 0; i < count; i++) {
|
||||
str.Append("\t");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
510
Substrate/SubstrateCS/Source/NBT/NBT.cs
Normal file
510
Substrate/SubstrateCS/Source/NBT/NBT.cs
Normal file
|
@ -0,0 +1,510 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace NBToolkit.Map.NBT
|
||||
{
|
||||
using Map.Utility;
|
||||
|
||||
public interface INBTObject<T>
|
||||
{
|
||||
T LoadTree (NBT_Value tree);
|
||||
T LoadTreeSafe (NBT_Value tree);
|
||||
|
||||
NBT_Value BuildTree ();
|
||||
|
||||
bool ValidateTree (NBT_Value tree);
|
||||
}
|
||||
|
||||
public class NBT_Tree : ICopyable<NBT_Tree>
|
||||
{
|
||||
private Stream _stream = null;
|
||||
private NBT_Compound _root = null;
|
||||
|
||||
private static NBT_Null _nulltag = new NBT_Null();
|
||||
|
||||
public NBT_Compound Root
|
||||
{
|
||||
get { return _root; }
|
||||
}
|
||||
|
||||
public NBT_Tree ()
|
||||
{
|
||||
_root = new NBT_Compound();
|
||||
}
|
||||
|
||||
public NBT_Tree (NBT_Compound tree)
|
||||
{
|
||||
_root = tree;
|
||||
}
|
||||
|
||||
public NBT_Tree (Stream s)
|
||||
{
|
||||
ReadFrom(s);
|
||||
}
|
||||
|
||||
public void ReadFrom (Stream s)
|
||||
{
|
||||
if (s != null) {
|
||||
_stream = s;
|
||||
_root = ReadRoot();
|
||||
_stream = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void WriteTo (Stream s)
|
||||
{
|
||||
if (s != null) {
|
||||
_stream = s;
|
||||
|
||||
if (_root != null) {
|
||||
WriteTag("", _root);
|
||||
}
|
||||
|
||||
_stream = null;
|
||||
}
|
||||
}
|
||||
|
||||
private NBT_Value ReadValue (NBT_Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case NBT_Type.TAG_END:
|
||||
return null;
|
||||
|
||||
case NBT_Type.TAG_BYTE:
|
||||
return ReadByte();
|
||||
|
||||
case NBT_Type.TAG_SHORT:
|
||||
return ReadShort();
|
||||
|
||||
case NBT_Type.TAG_INT:
|
||||
return ReadInt();
|
||||
|
||||
case NBT_Type.TAG_LONG:
|
||||
return ReadLong();
|
||||
|
||||
case NBT_Type.TAG_FLOAT:
|
||||
return ReadFloat();
|
||||
|
||||
case NBT_Type.TAG_DOUBLE:
|
||||
return ReadDouble();
|
||||
|
||||
case NBT_Type.TAG_BYTE_ARRAY:
|
||||
return ReadByteArray();
|
||||
|
||||
case NBT_Type.TAG_STRING:
|
||||
return ReadString();
|
||||
|
||||
case NBT_Type.TAG_LIST:
|
||||
return ReadList();
|
||||
|
||||
case NBT_Type.TAG_COMPOUND:
|
||||
return ReadCompound();
|
||||
}
|
||||
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
private NBT_Value ReadByte ()
|
||||
{
|
||||
int gzByte = _stream.ReadByte();
|
||||
if (gzByte == -1) {
|
||||
throw new NBTException(NBTException.MSG_GZIP_ENDOFSTREAM);
|
||||
}
|
||||
|
||||
NBT_Byte val = new NBT_Byte((byte)gzByte);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private NBT_Value ReadShort ()
|
||||
{
|
||||
byte[] gzBytes = new byte[2];
|
||||
_stream.Read(gzBytes, 0, 2);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(gzBytes);
|
||||
}
|
||||
|
||||
NBT_Short val = new NBT_Short(BitConverter.ToInt16(gzBytes, 0));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private NBT_Value ReadInt ()
|
||||
{
|
||||
byte[] gzBytes = new byte[4];
|
||||
_stream.Read(gzBytes, 0, 4);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(gzBytes);
|
||||
}
|
||||
|
||||
NBT_Int val = new NBT_Int(BitConverter.ToInt32(gzBytes, 0));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private NBT_Value ReadLong ()
|
||||
{
|
||||
byte[] gzBytes = new byte[8];
|
||||
_stream.Read(gzBytes, 0, 8);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(gzBytes);
|
||||
}
|
||||
|
||||
NBT_Long val = new NBT_Long(BitConverter.ToInt64(gzBytes, 0));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private NBT_Value ReadFloat ()
|
||||
{
|
||||
byte[] gzBytes = new byte[4];
|
||||
_stream.Read(gzBytes, 0, 4);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(gzBytes);
|
||||
}
|
||||
|
||||
NBT_Float val = new NBT_Float(BitConverter.ToSingle(gzBytes, 0));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private NBT_Value ReadDouble ()
|
||||
{
|
||||
byte[] gzBytes = new byte[8];
|
||||
_stream.Read(gzBytes, 0, 8);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(gzBytes);
|
||||
}
|
||||
|
||||
NBT_Double val = new NBT_Double(BitConverter.ToDouble(gzBytes, 0));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private NBT_Value ReadByteArray ()
|
||||
{
|
||||
byte[] lenBytes = new byte[4];
|
||||
_stream.Read(lenBytes, 0, 4);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(lenBytes);
|
||||
}
|
||||
|
||||
int length = BitConverter.ToInt32(lenBytes, 0);
|
||||
if (length < 0) {
|
||||
throw new NBTException(NBTException.MSG_READ_NEG);
|
||||
}
|
||||
|
||||
byte[] data = new byte[length];
|
||||
_stream.Read(data, 0, length);
|
||||
|
||||
NBT_ByteArray val = new NBT_ByteArray(data);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private NBT_Value ReadString ()
|
||||
{
|
||||
byte[] lenBytes = new byte[2];
|
||||
_stream.Read(lenBytes, 0, 2);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(lenBytes);
|
||||
}
|
||||
|
||||
short len = BitConverter.ToInt16(lenBytes, 0);
|
||||
if (len < 0) {
|
||||
throw new NBTException(NBTException.MSG_READ_NEG);
|
||||
}
|
||||
|
||||
byte[] strBytes = new byte[len];
|
||||
_stream.Read(strBytes, 0, len);
|
||||
|
||||
System.Text.Encoding str = Encoding.GetEncoding(28591);
|
||||
|
||||
NBT_String val = new NBT_String(str.GetString(strBytes));
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private NBT_Value ReadList ()
|
||||
{
|
||||
int gzByte = _stream.ReadByte();
|
||||
if (gzByte == -1) {
|
||||
throw new NBTException(NBTException.MSG_GZIP_ENDOFSTREAM);
|
||||
}
|
||||
|
||||
NBT_List val = new NBT_List((NBT_Type)gzByte);
|
||||
if (val.ValueType > (NBT_Type)Enum.GetValues(typeof(NBT_Type)).GetUpperBound(0)) {
|
||||
throw new NBTException(NBTException.MSG_READ_TYPE);
|
||||
}
|
||||
|
||||
byte[] lenBytes = new byte[4];
|
||||
_stream.Read(lenBytes, 0, 4);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(lenBytes);
|
||||
}
|
||||
|
||||
int length = BitConverter.ToInt32(lenBytes, 0);
|
||||
if (length < 0) {
|
||||
throw new NBTException(NBTException.MSG_READ_NEG);
|
||||
}
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
val.Add(ReadValue(val.ValueType));
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private NBT_Value ReadCompound ()
|
||||
{
|
||||
NBT_Compound val = new NBT_Compound();
|
||||
|
||||
while (ReadTag(val)) ;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
private NBT_Compound ReadRoot ()
|
||||
{
|
||||
NBT_Type type = (NBT_Type)_stream.ReadByte();
|
||||
if (type == NBT_Type.TAG_COMPOUND) {
|
||||
string name = ReadString().ToNBTString().Data;
|
||||
return ReadValue(type) as NBT_Compound;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool ReadTag (NBT_Compound parent)
|
||||
{
|
||||
//NBT_Tag tag = new NBT_Tag();
|
||||
|
||||
NBT_Type type = (NBT_Type)_stream.ReadByte();
|
||||
if (type != NBT_Type.TAG_END) {
|
||||
string name = ReadString().ToNBTString().Data;
|
||||
parent[name] = ReadValue(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
//tag.Value = ReadValue(type);
|
||||
|
||||
//return tag;
|
||||
}
|
||||
|
||||
private void WriteValue (NBT_Value val)
|
||||
{
|
||||
switch (val.GetNBTType()) {
|
||||
case NBT_Type.TAG_END:
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_BYTE:
|
||||
WriteByte(val.ToNBTByte());
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_SHORT:
|
||||
WriteShort(val.ToNBTShort());
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_INT:
|
||||
WriteInt(val.ToNBTInt());
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_LONG:
|
||||
WriteLong(val.ToNBTLong());
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_FLOAT:
|
||||
WriteFloat(val.ToNBTFloat());
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_DOUBLE:
|
||||
WriteDouble(val.ToNBTDouble());
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_BYTE_ARRAY:
|
||||
WriteByteArray(val.ToNBTByteArray());
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_STRING:
|
||||
WriteString(val.ToNBTString());
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_LIST:
|
||||
WriteList(val.ToNBTList());
|
||||
break;
|
||||
|
||||
case NBT_Type.TAG_COMPOUND:
|
||||
WriteCompound(val.ToNBTCompound());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteByte (NBT_Byte val)
|
||||
{
|
||||
_stream.WriteByte(val.Data);
|
||||
}
|
||||
|
||||
private void WriteShort (NBT_Short val)
|
||||
{
|
||||
byte[] gzBytes = BitConverter.GetBytes(val.Data);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(gzBytes);
|
||||
}
|
||||
|
||||
_stream.Write(gzBytes, 0, 2);
|
||||
}
|
||||
|
||||
private void WriteInt (NBT_Int val)
|
||||
{
|
||||
byte[] gzBytes = BitConverter.GetBytes(val.Data);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(gzBytes);
|
||||
}
|
||||
|
||||
_stream.Write(gzBytes, 0, 4);
|
||||
}
|
||||
|
||||
private void WriteLong (NBT_Long val)
|
||||
{
|
||||
byte[] gzBytes = BitConverter.GetBytes(val.Data);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(gzBytes);
|
||||
}
|
||||
|
||||
_stream.Write(gzBytes, 0, 8);
|
||||
}
|
||||
|
||||
private void WriteFloat (NBT_Float val)
|
||||
{
|
||||
byte[] gzBytes = BitConverter.GetBytes(val.Data);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(gzBytes);
|
||||
}
|
||||
|
||||
_stream.Write(gzBytes, 0, 4);
|
||||
}
|
||||
|
||||
private void WriteDouble (NBT_Double val)
|
||||
{
|
||||
byte[] gzBytes = BitConverter.GetBytes(val.Data);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(gzBytes);
|
||||
}
|
||||
|
||||
_stream.Write(gzBytes, 0, 8);
|
||||
}
|
||||
|
||||
private void WriteByteArray (NBT_ByteArray val)
|
||||
{
|
||||
byte[] lenBytes = BitConverter.GetBytes(val.Length);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(lenBytes);
|
||||
}
|
||||
|
||||
_stream.Write(lenBytes, 0, 4);
|
||||
_stream.Write(val.Data, 0, val.Length);
|
||||
}
|
||||
|
||||
private void WriteString (NBT_String val)
|
||||
{
|
||||
byte[] lenBytes = BitConverter.GetBytes((short)val.Length);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(lenBytes);
|
||||
}
|
||||
|
||||
_stream.Write(lenBytes, 0, 2);
|
||||
|
||||
System.Text.Encoding str = Encoding.GetEncoding(28591);
|
||||
byte[] gzBytes = str.GetBytes(val.Data);
|
||||
|
||||
_stream.Write(gzBytes, 0, gzBytes.Length);
|
||||
}
|
||||
|
||||
private void WriteList (NBT_List val)
|
||||
{
|
||||
byte[] lenBytes = BitConverter.GetBytes(val.Count);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(lenBytes);
|
||||
}
|
||||
|
||||
_stream.WriteByte((byte)val.ValueType);
|
||||
_stream.Write(lenBytes, 0, 4);
|
||||
|
||||
foreach (NBT_Value v in val) {
|
||||
WriteValue(v);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteCompound (NBT_Compound val)
|
||||
{
|
||||
foreach (KeyValuePair<string, NBT_Value> item in val) {
|
||||
WriteTag(item.Key, item.Value);
|
||||
}
|
||||
|
||||
WriteTag(null, _nulltag);
|
||||
}
|
||||
|
||||
private void WriteTag (string name, NBT_Value val)
|
||||
{
|
||||
_stream.WriteByte((byte)val.GetNBTType());
|
||||
|
||||
if (val.GetNBTType() != NBT_Type.TAG_END) {
|
||||
WriteString(name);
|
||||
WriteValue(val);
|
||||
}
|
||||
}
|
||||
|
||||
#region ICopyable<NBT_Tree> Members
|
||||
|
||||
public NBT_Tree Copy ()
|
||||
{
|
||||
NBT_Tree tree = new NBT_Tree();
|
||||
tree._root = _root.Copy() as NBT_Compound;
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class NBTException : Exception
|
||||
{
|
||||
public const String MSG_GZIP_ENDOFSTREAM = "Gzip Error: Unexpected end of stream";
|
||||
|
||||
public const String MSG_READ_NEG = "Read Error: Negative length";
|
||||
public const String MSG_READ_TYPE = "Read Error: Invalid value type";
|
||||
|
||||
public NBTException () { }
|
||||
|
||||
public NBTException (String msg) : base(msg) { }
|
||||
|
||||
public NBTException (String msg, Exception innerException) : base(msg, innerException) { }
|
||||
}
|
||||
|
||||
public class InvalidNBTObjectException : Exception { }
|
||||
|
||||
public class InvalidTagException : Exception { }
|
||||
|
||||
public class InvalidValueException : Exception { }
|
||||
}
|
295
Substrate/SubstrateCS/Source/NBT/NBTSchema.cs
Normal file
295
Substrate/SubstrateCS/Source/NBT/NBTSchema.cs
Normal file
|
@ -0,0 +1,295 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map.NBT
|
||||
{
|
||||
public abstract class NBTSchemaNode
|
||||
{
|
||||
private string _name;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return _name; }
|
||||
}
|
||||
|
||||
public NBTSchemaNode (string name)
|
||||
{
|
||||
_name = name;
|
||||
}
|
||||
|
||||
public virtual NBT_Value BuildDefaultTree ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class NBTScalerNode : NBTSchemaNode
|
||||
{
|
||||
private NBT_Type _type;
|
||||
|
||||
public NBT_Type Type
|
||||
{
|
||||
get { return _type; }
|
||||
}
|
||||
|
||||
public NBTScalerNode (string name, NBT_Type type)
|
||||
: base(name)
|
||||
{
|
||||
_type = type;
|
||||
}
|
||||
|
||||
public override NBT_Value BuildDefaultTree ()
|
||||
{
|
||||
switch (_type) {
|
||||
case NBT_Type.TAG_STRING:
|
||||
return new NBT_String();
|
||||
|
||||
case NBT_Type.TAG_BYTE:
|
||||
return new NBT_Byte();
|
||||
|
||||
case NBT_Type.TAG_SHORT:
|
||||
return new NBT_Short();
|
||||
|
||||
case NBT_Type.TAG_INT:
|
||||
return new NBT_Int();
|
||||
|
||||
case NBT_Type.TAG_LONG:
|
||||
return new NBT_Long();
|
||||
|
||||
case NBT_Type.TAG_FLOAT:
|
||||
return new NBT_Float();
|
||||
|
||||
case NBT_Type.TAG_DOUBLE:
|
||||
return new NBT_Double();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class NBTStringNode : NBTSchemaNode
|
||||
{
|
||||
private string _value = "";
|
||||
private int _length;
|
||||
|
||||
public int Length
|
||||
{
|
||||
get { return _length; }
|
||||
}
|
||||
|
||||
public string Value
|
||||
{
|
||||
get { return _value; }
|
||||
}
|
||||
|
||||
public NBTStringNode (string name, string value)
|
||||
: base(name)
|
||||
{
|
||||
_value = value;
|
||||
}
|
||||
|
||||
public NBTStringNode (string name, int length)
|
||||
: base(name)
|
||||
{
|
||||
_length = length;
|
||||
}
|
||||
|
||||
public override NBT_Value BuildDefaultTree ()
|
||||
{
|
||||
if (_value.Length > 0) {
|
||||
return new NBT_String(_value);
|
||||
}
|
||||
|
||||
return new NBT_String();
|
||||
}
|
||||
}
|
||||
|
||||
public class NBTArrayNode : NBTSchemaNode
|
||||
{
|
||||
private int _length;
|
||||
|
||||
public int Length
|
||||
{
|
||||
get { return _length; }
|
||||
}
|
||||
|
||||
public NBTArrayNode (string name)
|
||||
: base(name)
|
||||
{
|
||||
_length = 0;
|
||||
}
|
||||
|
||||
public NBTArrayNode (string name, int length)
|
||||
: base(name)
|
||||
{
|
||||
_length = length;
|
||||
}
|
||||
|
||||
public override NBT_Value BuildDefaultTree ()
|
||||
{
|
||||
return new NBT_ByteArray(new byte[_length]);
|
||||
}
|
||||
}
|
||||
|
||||
public class NBTListNode : NBTSchemaNode
|
||||
{
|
||||
private NBT_Type _type;
|
||||
private int _length;
|
||||
private NBTSchemaNode _subschema;
|
||||
|
||||
public int Length
|
||||
{
|
||||
get { return _length; }
|
||||
}
|
||||
|
||||
public NBT_Type Type
|
||||
{
|
||||
get { return _type; }
|
||||
}
|
||||
|
||||
public NBTSchemaNode SubSchema
|
||||
{
|
||||
get { return _subschema; }
|
||||
}
|
||||
|
||||
public NBTListNode (string name, NBT_Type type)
|
||||
: base(name)
|
||||
{
|
||||
_type = type;
|
||||
}
|
||||
|
||||
public NBTListNode (string name, NBT_Type type, int length)
|
||||
: base(name)
|
||||
{
|
||||
_type = type;
|
||||
_length = length;
|
||||
}
|
||||
|
||||
public NBTListNode (string name, NBT_Type type, NBTSchemaNode subschema)
|
||||
: base(name)
|
||||
{
|
||||
_type = type;
|
||||
_subschema = subschema;
|
||||
}
|
||||
|
||||
public NBTListNode (string name, NBT_Type type, int length, NBTSchemaNode subschema)
|
||||
: base(name)
|
||||
{
|
||||
_type = type;
|
||||
_length = length;
|
||||
_subschema = subschema;
|
||||
}
|
||||
|
||||
public override NBT_Value BuildDefaultTree ()
|
||||
{
|
||||
if (_length == 0) {
|
||||
return new NBT_List(_type);
|
||||
}
|
||||
|
||||
NBT_List list = new NBT_List(_type);
|
||||
for (int i = 0; i < _length; i++) {
|
||||
list.Add(_subschema.BuildDefaultTree());
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
public class NBTCompoundNode : NBTSchemaNode, ICollection<NBTSchemaNode>
|
||||
{
|
||||
private List<NBTSchemaNode> _subnodes;
|
||||
|
||||
#region ICollection<NBTSchemaNode> Members
|
||||
|
||||
public void Add (NBTSchemaNode item)
|
||||
{
|
||||
_subnodes.Add(item);
|
||||
}
|
||||
|
||||
public void Clear ()
|
||||
{
|
||||
_subnodes.Clear();
|
||||
}
|
||||
|
||||
public bool Contains (NBTSchemaNode item)
|
||||
{
|
||||
return _subnodes.Contains(item);
|
||||
}
|
||||
|
||||
public void CopyTo (NBTSchemaNode[] array, int arrayIndex)
|
||||
{
|
||||
_subnodes.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return _subnodes.Count; }
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool Remove (NBTSchemaNode item)
|
||||
{
|
||||
return _subnodes.Remove(item);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<NBTSchemaNode> Members
|
||||
|
||||
public IEnumerator<NBTSchemaNode> GetEnumerator ()
|
||||
{
|
||||
return _subnodes.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
|
||||
{
|
||||
return _subnodes.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public NBTCompoundNode ()
|
||||
: base("")
|
||||
{
|
||||
_subnodes = new List<NBTSchemaNode>();
|
||||
}
|
||||
|
||||
public NBTCompoundNode (string name)
|
||||
: base(name)
|
||||
{
|
||||
_subnodes = new List<NBTSchemaNode>();
|
||||
}
|
||||
|
||||
public NBTCompoundNode MergeInto (NBTCompoundNode tree)
|
||||
{
|
||||
foreach (NBTSchemaNode node in _subnodes) {
|
||||
NBTSchemaNode f = tree._subnodes.Find(n => n.Name == node.Name);
|
||||
if (f != null) {
|
||||
continue;
|
||||
}
|
||||
tree.Add(node);
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
public override NBT_Value BuildDefaultTree ()
|
||||
{
|
||||
NBT_Compound list = new NBT_Compound();
|
||||
foreach (NBTSchemaNode node in _subnodes) {
|
||||
list[node.Name] = node.BuildDefaultTree();
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
630
Substrate/SubstrateCS/Source/NBT/NBTValues.cs
Normal file
630
Substrate/SubstrateCS/Source/NBT/NBTValues.cs
Normal file
|
@ -0,0 +1,630 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map.NBT {
|
||||
|
||||
using Map.Utility;
|
||||
|
||||
/// <summary>
|
||||
/// Describes the type of value held by an NBT_Tag
|
||||
/// </summary>
|
||||
public enum NBT_Type
|
||||
{
|
||||
TAG_END = 0,
|
||||
TAG_BYTE = 1, // 8 bits signed
|
||||
TAG_SHORT = 2, // 16 bits signed
|
||||
TAG_INT = 3, // 32 bits signed
|
||||
TAG_LONG = 4, // 64 bits signed
|
||||
TAG_FLOAT = 5,
|
||||
TAG_DOUBLE = 6,
|
||||
TAG_BYTE_ARRAY = 7,
|
||||
TAG_STRING = 8,
|
||||
TAG_LIST = 9,
|
||||
TAG_COMPOUND = 10
|
||||
}
|
||||
|
||||
public abstract class NBT_Value : ICopyable<NBT_Value>
|
||||
{
|
||||
virtual public NBT_Null ToNBTNull () { throw new InvalidCastException(); }
|
||||
virtual public NBT_Byte ToNBTByte () { throw new InvalidCastException(); }
|
||||
virtual public NBT_Short ToNBTShort () { throw new InvalidCastException(); }
|
||||
virtual public NBT_Int ToNBTInt () { throw new InvalidCastException(); }
|
||||
virtual public NBT_Long ToNBTLong () { throw new InvalidCastException(); }
|
||||
virtual public NBT_Float ToNBTFloat () { throw new InvalidCastException(); }
|
||||
virtual public NBT_Double ToNBTDouble () { throw new InvalidCastException(); }
|
||||
virtual public NBT_ByteArray ToNBTByteArray () { throw new InvalidCastException(); }
|
||||
virtual public NBT_String ToNBTString () { throw new InvalidCastException(); }
|
||||
virtual public NBT_List ToNBTList () { throw new InvalidCastException(); }
|
||||
virtual public NBT_Compound ToNBTCompound () { throw new InvalidCastException(); }
|
||||
|
||||
virtual public NBT_Type GetNBTType () { return NBT_Type.TAG_END; }
|
||||
|
||||
public virtual NBT_Value Copy ()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class NBT_Null : NBT_Value
|
||||
{
|
||||
override public NBT_Null ToNBTNull () { return this; }
|
||||
override public NBT_Type GetNBTType () { return NBT_Type.TAG_END; }
|
||||
|
||||
public override NBT_Value Copy ()
|
||||
{
|
||||
return new NBT_Null();
|
||||
}
|
||||
}
|
||||
|
||||
public class NBT_Byte : NBT_Value
|
||||
{
|
||||
private byte _data = 0;
|
||||
|
||||
override public NBT_Byte ToNBTByte () { return this; }
|
||||
override public NBT_Type GetNBTType () { return NBT_Type.TAG_BYTE; }
|
||||
|
||||
public byte Data
|
||||
{
|
||||
get { return _data; }
|
||||
set { _data = value; }
|
||||
}
|
||||
|
||||
public NBT_Byte () { }
|
||||
|
||||
public NBT_Byte (byte d)
|
||||
{
|
||||
_data = d;
|
||||
}
|
||||
|
||||
public override NBT_Value Copy ()
|
||||
{
|
||||
return new NBT_Byte(_data);
|
||||
}
|
||||
|
||||
public static implicit operator NBT_Byte (byte b)
|
||||
{
|
||||
return new NBT_Byte(b);
|
||||
}
|
||||
|
||||
public static implicit operator byte (NBT_Byte b)
|
||||
{
|
||||
return b._data;
|
||||
}
|
||||
}
|
||||
|
||||
public class NBT_Short : NBT_Value
|
||||
{
|
||||
private short _data = 0;
|
||||
|
||||
override public NBT_Short ToNBTShort () { return this; }
|
||||
override public NBT_Type GetNBTType () { return NBT_Type.TAG_SHORT; }
|
||||
|
||||
public short Data
|
||||
{
|
||||
get { return _data; }
|
||||
set { _data = value; }
|
||||
}
|
||||
|
||||
public NBT_Short () { }
|
||||
|
||||
public NBT_Short (short d)
|
||||
{
|
||||
_data = d;
|
||||
}
|
||||
|
||||
public override NBT_Value Copy ()
|
||||
{
|
||||
return new NBT_Short(_data);
|
||||
}
|
||||
|
||||
public static implicit operator NBT_Short (short s)
|
||||
{
|
||||
return new NBT_Short(s);
|
||||
}
|
||||
|
||||
public static implicit operator short (NBT_Short s)
|
||||
{
|
||||
return s._data;
|
||||
}
|
||||
}
|
||||
|
||||
public class NBT_Int : NBT_Value
|
||||
{
|
||||
private int _data = 0;
|
||||
|
||||
override public NBT_Int ToNBTInt () { return this; }
|
||||
override public NBT_Type GetNBTType () { return NBT_Type.TAG_INT; }
|
||||
|
||||
public int Data
|
||||
{
|
||||
get { return _data; }
|
||||
set { _data = value; }
|
||||
}
|
||||
|
||||
public NBT_Int () { }
|
||||
|
||||
public NBT_Int (int d)
|
||||
{
|
||||
_data = d;
|
||||
}
|
||||
|
||||
public override NBT_Value Copy ()
|
||||
{
|
||||
return new NBT_Int(_data);
|
||||
}
|
||||
|
||||
public static implicit operator NBT_Int (int i)
|
||||
{
|
||||
return new NBT_Int(i);
|
||||
}
|
||||
|
||||
public static implicit operator int (NBT_Int i)
|
||||
{
|
||||
return i._data;
|
||||
}
|
||||
}
|
||||
|
||||
public class NBT_Long : NBT_Value
|
||||
{
|
||||
private long _data = 0;
|
||||
|
||||
override public NBT_Long ToNBTLong () { return this; }
|
||||
override public NBT_Type GetNBTType () { return NBT_Type.TAG_LONG; }
|
||||
|
||||
public long Data
|
||||
{
|
||||
get { return _data; }
|
||||
set { _data = value; }
|
||||
}
|
||||
|
||||
public NBT_Long () { }
|
||||
|
||||
public NBT_Long (long d)
|
||||
{
|
||||
_data = d;
|
||||
}
|
||||
|
||||
public override NBT_Value Copy ()
|
||||
{
|
||||
return new NBT_Long(_data);
|
||||
}
|
||||
|
||||
public static implicit operator NBT_Long (long l)
|
||||
{
|
||||
return new NBT_Long(l);
|
||||
}
|
||||
|
||||
public static implicit operator long (NBT_Long l)
|
||||
{
|
||||
return l._data;
|
||||
}
|
||||
}
|
||||
|
||||
public class NBT_Float : NBT_Value
|
||||
{
|
||||
private float _data = 0;
|
||||
|
||||
override public NBT_Float ToNBTFloat () { return this; }
|
||||
override public NBT_Type GetNBTType () { return NBT_Type.TAG_FLOAT; }
|
||||
|
||||
public float Data
|
||||
{
|
||||
get { return _data; }
|
||||
set { _data = value; }
|
||||
}
|
||||
|
||||
public NBT_Float () { }
|
||||
|
||||
public NBT_Float (float d)
|
||||
{
|
||||
_data = d;
|
||||
}
|
||||
|
||||
public override NBT_Value Copy ()
|
||||
{
|
||||
return new NBT_Float(_data);
|
||||
}
|
||||
|
||||
public static implicit operator NBT_Float (float f)
|
||||
{
|
||||
return new NBT_Float(f);
|
||||
}
|
||||
|
||||
public static implicit operator float (NBT_Float f)
|
||||
{
|
||||
return f._data;
|
||||
}
|
||||
}
|
||||
|
||||
public class NBT_Double : NBT_Value
|
||||
{
|
||||
private double _data = 0;
|
||||
|
||||
override public NBT_Double ToNBTDouble () { return this; }
|
||||
override public NBT_Type GetNBTType () { return NBT_Type.TAG_DOUBLE; }
|
||||
|
||||
public double Data
|
||||
{
|
||||
get { return _data; }
|
||||
set { _data = value; }
|
||||
}
|
||||
|
||||
public NBT_Double () { }
|
||||
|
||||
public NBT_Double (double d)
|
||||
{
|
||||
_data = d;
|
||||
}
|
||||
|
||||
public override NBT_Value Copy ()
|
||||
{
|
||||
return new NBT_Double(_data);
|
||||
}
|
||||
|
||||
public static implicit operator NBT_Double (double d)
|
||||
{
|
||||
return new NBT_Double(d);
|
||||
}
|
||||
|
||||
public static implicit operator double (NBT_Double d)
|
||||
{
|
||||
return d._data;
|
||||
}
|
||||
}
|
||||
|
||||
public class NBT_ByteArray : NBT_Value
|
||||
{
|
||||
private byte[] _data = null;
|
||||
|
||||
override public NBT_ByteArray ToNBTByteArray () { return this; }
|
||||
override public NBT_Type GetNBTType () { return NBT_Type.TAG_BYTE_ARRAY; }
|
||||
|
||||
public byte[] Data
|
||||
{
|
||||
get { return _data; }
|
||||
set { _data = value; }
|
||||
}
|
||||
|
||||
public int Length
|
||||
{
|
||||
get { return _data.Length; }
|
||||
}
|
||||
|
||||
public NBT_ByteArray () { }
|
||||
|
||||
public NBT_ByteArray (byte[] d)
|
||||
{
|
||||
_data = d;
|
||||
}
|
||||
|
||||
public override NBT_Value Copy ()
|
||||
{
|
||||
byte[] arr = new byte[_data.Length];
|
||||
_data.CopyTo(arr, 0);
|
||||
|
||||
return new NBT_ByteArray(arr);
|
||||
}
|
||||
|
||||
public byte this [int index] {
|
||||
get { return _data[index]; }
|
||||
set { _data[index] = value; }
|
||||
}
|
||||
|
||||
public static implicit operator NBT_ByteArray (byte[] b)
|
||||
{
|
||||
return new NBT_ByteArray(b);
|
||||
}
|
||||
}
|
||||
|
||||
public class NBT_String : NBT_Value
|
||||
{
|
||||
private string _data = "";
|
||||
|
||||
override public NBT_String ToNBTString () { return this; }
|
||||
override public NBT_Type GetNBTType () { return NBT_Type.TAG_STRING; }
|
||||
|
||||
public string Data
|
||||
{
|
||||
get { return _data; }
|
||||
set { _data = value; }
|
||||
}
|
||||
|
||||
public int Length
|
||||
{
|
||||
get { return _data.Length; }
|
||||
}
|
||||
|
||||
public NBT_String () { }
|
||||
|
||||
public NBT_String (string d)
|
||||
{
|
||||
_data = d;
|
||||
}
|
||||
|
||||
public override NBT_Value Copy ()
|
||||
{
|
||||
return new NBT_String(_data);
|
||||
}
|
||||
|
||||
public static implicit operator NBT_String (string s)
|
||||
{
|
||||
return new NBT_String(s);
|
||||
}
|
||||
|
||||
public static implicit operator string (NBT_String s)
|
||||
{
|
||||
return s._data;
|
||||
}
|
||||
}
|
||||
|
||||
public class NBT_List : NBT_Value, IList<NBT_Value>
|
||||
{
|
||||
private NBT_Type _type = NBT_Type.TAG_END;
|
||||
|
||||
private List<NBT_Value> _items = null;
|
||||
|
||||
override public NBT_List ToNBTList () { return this; }
|
||||
override public NBT_Type GetNBTType () { return NBT_Type.TAG_LIST; }
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return _items.Count; }
|
||||
}
|
||||
|
||||
public NBT_Type ValueType
|
||||
{
|
||||
get { return _type; }
|
||||
}
|
||||
|
||||
public NBT_List (NBT_Type type)
|
||||
{
|
||||
_type = type;
|
||||
_items = new List<NBT_Value>();
|
||||
}
|
||||
|
||||
public NBT_List (NBT_Type type, List<NBT_Value> items)
|
||||
{
|
||||
_type = type;
|
||||
_items = items;
|
||||
}
|
||||
|
||||
public override NBT_Value Copy ()
|
||||
{
|
||||
NBT_List list = new NBT_List(_type);
|
||||
foreach (NBT_Value item in _items) {
|
||||
list.Add(item.Copy());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
#region IList<NBT_Value> Members
|
||||
|
||||
public int IndexOf (NBT_Value item)
|
||||
{
|
||||
return _items.IndexOf(item);
|
||||
}
|
||||
|
||||
public void Insert (int index, NBT_Value item)
|
||||
{
|
||||
_items.Insert(index, item);
|
||||
}
|
||||
|
||||
public void RemoveAt (int index)
|
||||
{
|
||||
_items.RemoveAt(index);
|
||||
}
|
||||
|
||||
public NBT_Value this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return _items[index];
|
||||
}
|
||||
set
|
||||
{
|
||||
_items[index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICollection<NBT_Value> Members
|
||||
|
||||
public void Add (NBT_Value item)
|
||||
{
|
||||
_items.Add(item);
|
||||
}
|
||||
|
||||
public void Clear ()
|
||||
{
|
||||
_items.Clear();
|
||||
}
|
||||
|
||||
public bool Contains (NBT_Value item)
|
||||
{
|
||||
return _items.Contains(item);
|
||||
}
|
||||
|
||||
public void CopyTo (NBT_Value[] array, int arrayIndex)
|
||||
{
|
||||
_items.CopyTo(array, arrayIndex);
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool Remove (NBT_Value item)
|
||||
{
|
||||
return _items.Remove(item);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<NBT_Value> Members
|
||||
|
||||
public IEnumerator<NBT_Value> GetEnumerator ()
|
||||
{
|
||||
return _items.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
|
||||
{
|
||||
return _items.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class NBT_Compound : NBT_Value, IDictionary<string, NBT_Value>
|
||||
{
|
||||
private Dictionary<string, NBT_Value> _tags;
|
||||
|
||||
override public NBT_Compound ToNBTCompound () { return this; }
|
||||
override public NBT_Type GetNBTType () { return NBT_Type.TAG_COMPOUND; }
|
||||
|
||||
public int Count
|
||||
{
|
||||
get { return _tags.Count; }
|
||||
}
|
||||
|
||||
public NBT_Compound ()
|
||||
{
|
||||
_tags = new Dictionary<string, NBT_Value>();
|
||||
}
|
||||
|
||||
public override NBT_Value Copy ()
|
||||
{
|
||||
NBT_Compound list = new NBT_Compound();
|
||||
foreach (KeyValuePair<string, NBT_Value> item in _tags) {
|
||||
list[item.Key] = item.Value.Copy();
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
#region IDictionary<string,NBT_Value> Members
|
||||
|
||||
public void Add (string key, NBT_Value value)
|
||||
{
|
||||
_tags.Add(key, value);
|
||||
}
|
||||
|
||||
public bool ContainsKey (string key)
|
||||
{
|
||||
return _tags.ContainsKey(key);
|
||||
}
|
||||
|
||||
public ICollection<string> Keys
|
||||
{
|
||||
get { return _tags.Keys; }
|
||||
}
|
||||
|
||||
public bool Remove (string key)
|
||||
{
|
||||
return _tags.Remove(key);
|
||||
}
|
||||
|
||||
public bool TryGetValue (string key, out NBT_Value value)
|
||||
{
|
||||
return _tags.TryGetValue(key, out value);
|
||||
}
|
||||
|
||||
public ICollection<NBT_Value> Values
|
||||
{
|
||||
get { return _tags.Values; }
|
||||
}
|
||||
|
||||
public NBT_Value this[string key]
|
||||
{
|
||||
get
|
||||
{
|
||||
return _tags[key];
|
||||
}
|
||||
set
|
||||
{
|
||||
_tags[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ICollection<KeyValuePair<string,NBT_Value>> Members
|
||||
|
||||
public void Add (KeyValuePair<string, NBT_Value> item)
|
||||
{
|
||||
_tags.Add(item.Key, item.Value);
|
||||
}
|
||||
|
||||
public void Clear ()
|
||||
{
|
||||
_tags.Clear();
|
||||
}
|
||||
|
||||
public bool Contains (KeyValuePair<string, NBT_Value> item)
|
||||
{
|
||||
NBT_Value value;
|
||||
if (!_tags.TryGetValue(item.Key, out value)) {
|
||||
return false;
|
||||
}
|
||||
return value == item.Value;
|
||||
}
|
||||
|
||||
public void CopyTo (KeyValuePair<string, NBT_Value>[] array, int arrayIndex)
|
||||
{
|
||||
if (array == null) {
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
if (arrayIndex < 0) {
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
if (array.Length - arrayIndex < _tags.Count) {
|
||||
throw new ArgumentException();
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, NBT_Value> item in _tags) {
|
||||
array[arrayIndex] = item;
|
||||
arrayIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
public bool Remove (KeyValuePair<string, NBT_Value> item)
|
||||
{
|
||||
if (Contains(item)) {
|
||||
_tags.Remove(item.Key);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable<KeyValuePair<string,NBT_Value>> Members
|
||||
|
||||
public IEnumerator<KeyValuePair<string, NBT_Value>> GetEnumerator ()
|
||||
{
|
||||
return _tags.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IEnumerable Members
|
||||
|
||||
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ()
|
||||
{
|
||||
return _tags.GetEnumerator();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
241
Substrate/SubstrateCS/Source/NBT/NBTVerifier.cs
Normal file
241
Substrate/SubstrateCS/Source/NBT/NBTVerifier.cs
Normal file
|
@ -0,0 +1,241 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map.NBT
|
||||
{
|
||||
public delegate void MissingTagHandler (Object o, TagEventArgs e);
|
||||
public delegate void InvalidTagTypeHandler (Object o, TagEventArgs e);
|
||||
public delegate void InvalidTagValueHandler (Object o, TagEventArgs e);
|
||||
|
||||
public interface INBTVerifier
|
||||
{
|
||||
event MissingTagHandler MissingTag;
|
||||
event InvalidTagTypeHandler InvalidTagType;
|
||||
event InvalidTagValueHandler InvalidTagValue;
|
||||
|
||||
bool Verify ();
|
||||
}
|
||||
|
||||
public class TagEventArgs : EventArgs
|
||||
{
|
||||
protected string _tagName;
|
||||
protected NBT_Value _tag;
|
||||
protected NBTSchemaNode _schema;
|
||||
|
||||
public string TagName
|
||||
{
|
||||
get { return _tagName; }
|
||||
}
|
||||
|
||||
public TagEventArgs (string tagName)
|
||||
: base()
|
||||
{
|
||||
_tagName = tagName;
|
||||
}
|
||||
|
||||
public TagEventArgs (string tagName, NBT_Value tag)
|
||||
: base()
|
||||
{
|
||||
_tag = tag;
|
||||
_tagName = tagName;
|
||||
}
|
||||
|
||||
public TagEventArgs (NBTSchemaNode schema, NBT_Value tag)
|
||||
: base()
|
||||
{
|
||||
_tag = tag;
|
||||
_schema = schema;
|
||||
}
|
||||
}
|
||||
|
||||
public class NBTVerifier : INBTVerifier
|
||||
{
|
||||
private NBT_Value _root;
|
||||
private NBTSchemaNode _schema;
|
||||
|
||||
public event MissingTagHandler MissingTag;
|
||||
public event InvalidTagTypeHandler InvalidTagType;
|
||||
public event InvalidTagValueHandler InvalidTagValue;
|
||||
|
||||
public NBTVerifier () { }
|
||||
|
||||
public NBTVerifier (NBT_Value root, NBTSchemaNode schema)
|
||||
{
|
||||
_root = root;
|
||||
_schema = schema;
|
||||
}
|
||||
|
||||
public bool Verify ()
|
||||
{
|
||||
return Verify(_root, _schema);
|
||||
}
|
||||
|
||||
static NBTCompoundNode inventorySchema = new NBTCompoundNode("")
|
||||
{
|
||||
new NBTScalerNode("id", NBT_Type.TAG_SHORT),
|
||||
new NBTScalerNode("Damage", NBT_Type.TAG_SHORT),
|
||||
new NBTScalerNode("Count", NBT_Type.TAG_BYTE),
|
||||
new NBTScalerNode("Slot", NBT_Type.TAG_BYTE),
|
||||
};
|
||||
|
||||
private bool Verify (NBT_Value tag, NBTSchemaNode schema)
|
||||
{
|
||||
if (tag == null) {
|
||||
OnMissingTag(new TagEventArgs(schema.Name));
|
||||
return false;
|
||||
}
|
||||
|
||||
NBTScalerNode scaler = schema as NBTScalerNode;
|
||||
if (scaler != null) {
|
||||
return VerifyScaler(tag, scaler);
|
||||
}
|
||||
|
||||
NBTStringNode str = schema as NBTStringNode;
|
||||
if (str != null) {
|
||||
return VerifyString(tag, str);
|
||||
}
|
||||
|
||||
NBTArrayNode array = schema as NBTArrayNode;
|
||||
if (array != null) {
|
||||
return VerifyArray(tag, array);
|
||||
}
|
||||
|
||||
NBTListNode list = schema as NBTListNode;
|
||||
if (list != null) {
|
||||
return VerifyList(tag, list);
|
||||
}
|
||||
|
||||
NBTCompoundNode compound = schema as NBTCompoundNode;
|
||||
if (compound != null) {
|
||||
return VerifyCompound(tag, compound);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool VerifyScaler (NBT_Value tag, NBTScalerNode schema)
|
||||
{
|
||||
if (tag.GetNBTType() != schema.Type) {
|
||||
OnInvalidTagType(new TagEventArgs(schema.Name, tag));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool VerifyString (NBT_Value tag, NBTStringNode schema)
|
||||
{
|
||||
NBT_String stag = tag as NBT_String;
|
||||
if (stag == null) {
|
||||
OnInvalidTagType(new TagEventArgs(schema, tag));
|
||||
return false;
|
||||
}
|
||||
if (schema.Length > 0 && stag.Length > schema.Length) {
|
||||
OnInvalidTagValue(new TagEventArgs(schema, tag));
|
||||
return false;
|
||||
}
|
||||
if (schema.Value != null && stag.Data != schema.Value) {
|
||||
OnInvalidTagValue(new TagEventArgs(schema, tag));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private bool VerifyArray (NBT_Value tag, NBTArrayNode schema)
|
||||
{
|
||||
NBT_ByteArray atag = tag as NBT_ByteArray;
|
||||
if (atag == null) {
|
||||
OnInvalidTagType(new TagEventArgs(schema, tag));
|
||||
return false;
|
||||
}
|
||||
if (schema.Length > 0 && atag.Length != schema.Length) {
|
||||
OnInvalidTagValue(new TagEventArgs(schema, tag));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool VerifyList (NBT_Value tag, NBTListNode schema)
|
||||
{
|
||||
NBT_List ltag = tag as NBT_List;
|
||||
if (ltag == null) {
|
||||
OnInvalidTagType(new TagEventArgs(schema, tag));
|
||||
return false;
|
||||
}
|
||||
if (ltag.Count > 0 && ltag.ValueType != schema.Type) {
|
||||
OnInvalidTagValue(new TagEventArgs(schema, tag));
|
||||
return false;
|
||||
}
|
||||
if (schema.Length > 0 && ltag.Count != schema.Length) {
|
||||
OnInvalidTagValue(new TagEventArgs(schema, tag));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Patch up empty lists
|
||||
//if (schema.Length == 0) {
|
||||
// tag = new NBT_List(schema.Type);
|
||||
//}
|
||||
|
||||
bool pass = true;
|
||||
|
||||
// If a subschema is set, test all items in list against it
|
||||
|
||||
if (schema.SubSchema != null) {
|
||||
foreach (NBT_Value v in ltag) {
|
||||
pass = Verify(v, schema.SubSchema) && pass;
|
||||
}
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
private bool VerifyCompound (NBT_Value tag, NBTCompoundNode schema)
|
||||
{
|
||||
NBT_Compound ctag = tag as NBT_Compound;
|
||||
if (ctag == null) {
|
||||
OnInvalidTagType(new TagEventArgs(schema, tag));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool pass = true;
|
||||
|
||||
foreach (NBTSchemaNode node in schema) {
|
||||
NBT_Value value;
|
||||
ctag.TryGetValue(node.Name, out value);
|
||||
|
||||
pass = Verify(value, node) && pass;
|
||||
}
|
||||
|
||||
return pass;
|
||||
}
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
protected void OnMissingTag (TagEventArgs e)
|
||||
{
|
||||
if (MissingTag != null) {
|
||||
MissingTag(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnInvalidTagType (TagEventArgs e)
|
||||
{
|
||||
if (InvalidTagType != null) {
|
||||
InvalidTagType(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnInvalidTagValue (TagEventArgs e)
|
||||
{
|
||||
if (InvalidTagValue != null) {
|
||||
InvalidTagValue(this, e);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
334
Substrate/SubstrateCS/Source/Region.cs
Normal file
334
Substrate/SubstrateCS/Source/Region.cs
Normal file
|
@ -0,0 +1,334 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
using NBT;
|
||||
|
||||
public class Region : IDisposable, IChunkContainer, IChunkCache
|
||||
{
|
||||
protected int _rx;
|
||||
protected int _rz;
|
||||
protected bool _disposed = false;
|
||||
|
||||
protected RegionManager _regionMan;
|
||||
|
||||
protected static Regex _namePattern = new Regex("r\\.(-?[0-9]+)\\.(-?[0-9]+)\\.mcr$");
|
||||
|
||||
protected WeakReference _regionFile;
|
||||
|
||||
protected Dictionary<ChunkKey, WeakReference> _cache;
|
||||
protected Dictionary<ChunkKey, ChunkRef> _dirty;
|
||||
|
||||
public int X
|
||||
{
|
||||
get { return _rx; }
|
||||
}
|
||||
|
||||
public int Z
|
||||
{
|
||||
get { return _rz; }
|
||||
}
|
||||
|
||||
public Region (RegionManager rm, int rx, int rz)
|
||||
{
|
||||
_regionMan = rm;
|
||||
_regionFile = new WeakReference(null);
|
||||
_rx = rx;
|
||||
_rz = rz;
|
||||
|
||||
_cache = new Dictionary<ChunkKey, WeakReference>();
|
||||
_dirty = new Dictionary<ChunkKey, ChunkRef>();
|
||||
|
||||
if (!File.Exists(GetFilePath())) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
public Region (RegionManager rm, string filename)
|
||||
{
|
||||
_regionMan = rm;
|
||||
_regionFile = new WeakReference(null);
|
||||
|
||||
ParseFileName(filename, out _rx, out _rz);
|
||||
|
||||
if (!File.Exists(Path.Combine(_regionMan.GetRegionPath(), filename))) {
|
||||
throw new FileNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
~Region ()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
Dispose(true);
|
||||
System.GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose (bool disposing)
|
||||
{
|
||||
if (!_disposed) {
|
||||
if (disposing) {
|
||||
// Cleanup managed resources
|
||||
RegionFile rf = _regionFile.Target as RegionFile;
|
||||
if (rf != null) {
|
||||
rf.Dispose();
|
||||
rf = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup unmanaged resources
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
public string GetFileName ()
|
||||
{
|
||||
return "r." + _rx + "." + _rz + ".mcr";
|
||||
|
||||
}
|
||||
|
||||
public static bool TestFileName (string filename)
|
||||
{
|
||||
Match match = _namePattern.Match(filename);
|
||||
if (!match.Success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static bool ParseFileName (string filename, out int x, out int z)
|
||||
{
|
||||
x = 0;
|
||||
z = 0;
|
||||
|
||||
Match match = _namePattern.Match(filename);
|
||||
if (!match.Success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
x = Convert.ToInt32(match.Groups[1].Value);
|
||||
z = Convert.ToInt32(match.Groups[2].Value);
|
||||
return true;
|
||||
}
|
||||
|
||||
public string GetFilePath ()
|
||||
{
|
||||
return System.IO.Path.Combine(_regionMan.GetRegionPath(), GetFileName());
|
||||
}
|
||||
|
||||
protected RegionFile GetRegionFile ()
|
||||
{
|
||||
RegionFile rf = _regionFile.Target as RegionFile;
|
||||
if (rf == null) {
|
||||
rf = new RegionFile(GetFilePath());
|
||||
_regionFile.Target = rf;
|
||||
}
|
||||
|
||||
return rf;
|
||||
}
|
||||
|
||||
public NBT_Tree GetChunkTree (int lcx, int lcz)
|
||||
{
|
||||
RegionFile rf = GetRegionFile();
|
||||
Stream nbtstr = rf.GetChunkDataInputStream(lcx, lcz);
|
||||
if (nbtstr == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new NBT_Tree(nbtstr);
|
||||
}
|
||||
|
||||
public bool SaveChunkTree (int lcx, int lcz, NBT_Tree tree)
|
||||
{
|
||||
RegionFile rf = GetRegionFile();
|
||||
Stream zipstr = rf.GetChunkDataOutputStream(lcx, lcz);
|
||||
if (zipstr == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tree.WriteTo(zipstr);
|
||||
zipstr.Close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public Stream GetChunkOutStream (int lcx, int lcz)
|
||||
{
|
||||
RegionFile rf = GetRegionFile();
|
||||
return rf.GetChunkDataOutputStream(lcx, lcz);
|
||||
}
|
||||
|
||||
public int ChunkCount ()
|
||||
{
|
||||
RegionFile rf = GetRegionFile();
|
||||
|
||||
int count = 0;
|
||||
for (int x = 0; x < ChunkManager.REGION_XLEN; x++) {
|
||||
for (int z = 0; z < ChunkManager.REGION_ZLEN; z++) {
|
||||
if (rf.HasChunk(x, z)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
public ChunkRef GetChunkRef (int lcx, int lcz, IChunkCache cache)
|
||||
{
|
||||
ChunkKey k = new ChunkKey(lcx, lcz);
|
||||
|
||||
ChunkRef c = null;
|
||||
|
||||
WeakReference chunkref = null;
|
||||
if (_cache.TryGetValue(k, out chunkref)) {
|
||||
c = chunkref.Target as ChunkRef;
|
||||
}
|
||||
else {
|
||||
_cache.Add(k, new WeakReference(null));
|
||||
}
|
||||
|
||||
if (c != null) {
|
||||
return c;
|
||||
}
|
||||
|
||||
try {
|
||||
c = new ChunkRef(this, cache, lcx, lcz);
|
||||
_cache[k].Target = c;
|
||||
return c;
|
||||
}
|
||||
catch (MissingChunkException) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region IChunkCollection Members
|
||||
|
||||
public int ChunkGlobalX (int cx)
|
||||
{
|
||||
return _rx * ChunkManager.REGION_XLEN + cx;
|
||||
}
|
||||
|
||||
public int ChunkGlobalZ (int cz)
|
||||
{
|
||||
return _rz * ChunkManager.REGION_ZLEN + cz;
|
||||
}
|
||||
|
||||
public int ChunkLocalX (int cx)
|
||||
{
|
||||
return cx;
|
||||
}
|
||||
|
||||
public int ChunkLocalZ (int cz)
|
||||
{
|
||||
return cz;
|
||||
}
|
||||
|
||||
public Chunk GetChunk (int lcx, int lcz)
|
||||
{
|
||||
if (!ChunkExists(lcx, lcz)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Chunk(GetChunkTree(lcx, lcz));
|
||||
}
|
||||
|
||||
public ChunkRef GetChunkRef (int lcx, int lcz)
|
||||
{
|
||||
return GetChunkRef(lcx, lcz, this);
|
||||
}
|
||||
|
||||
public bool ChunkExists (int lcx, int lcz)
|
||||
{
|
||||
RegionFile rf = GetRegionFile();
|
||||
return rf.HasChunk(lcx, lcz);
|
||||
}
|
||||
|
||||
public bool DeleteChunk (int lcx, int lcz)
|
||||
{
|
||||
RegionFile rf = GetRegionFile();
|
||||
if (!rf.HasChunk(lcx, lcz)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rf.DeleteChunk(lcx, lcz);
|
||||
|
||||
ChunkKey k = new ChunkKey(lcx, lcz);
|
||||
_cache.Remove(k);
|
||||
_dirty.Remove(k);
|
||||
|
||||
if (ChunkCount() == 0) {
|
||||
_regionMan.DeleteRegion(X, Z);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public int Save ()
|
||||
{
|
||||
int saved = 0;
|
||||
foreach (ChunkRef c in _dirty.Values) {
|
||||
int lcx = c.LocalX;
|
||||
int lcz = c.LocalZ;
|
||||
|
||||
if (!ChunkExists(lcx, lcz)) {
|
||||
throw new MissingChunkException();
|
||||
}
|
||||
|
||||
if (c.Save(GetChunkOutStream(lcx, lcz))) {
|
||||
saved++;
|
||||
}
|
||||
}
|
||||
|
||||
_dirty.Clear();
|
||||
return saved;
|
||||
}
|
||||
|
||||
public bool SaveChunk (Chunk chunk)
|
||||
{
|
||||
return chunk.Save(GetChunkOutStream(ChunkLocalX(chunk.X), ChunkLocalZ(chunk.Z)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region IChunkCache Members
|
||||
|
||||
public bool MarkChunkDirty (ChunkRef chunk)
|
||||
{
|
||||
int lcx = chunk.LocalX;
|
||||
int lcz = chunk.LocalZ;
|
||||
|
||||
ChunkKey k = new ChunkKey(lcx, lcz);
|
||||
if (!_dirty.ContainsKey(k)) {
|
||||
_dirty.Add(k, GetChunkRef(lcx, lcz));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool MarkChunkClean (ChunkRef chunk)
|
||||
{
|
||||
int lcx = chunk.LocalX;
|
||||
int lcz = chunk.LocalZ;
|
||||
|
||||
ChunkKey k = new ChunkKey(lcx, lcz);
|
||||
if (_dirty.ContainsKey(k)) {
|
||||
_dirty.Remove(k);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
131
Substrate/SubstrateCS/Source/RegionEnumerator.cs
Normal file
131
Substrate/SubstrateCS/Source/RegionEnumerator.cs
Normal file
|
@ -0,0 +1,131 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.IO;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
public class RegionList : IEnumerable<Region>
|
||||
{
|
||||
private List<Region> _regions;
|
||||
|
||||
public RegionList (List<Region> regs)
|
||||
{
|
||||
_regions = regs;
|
||||
}
|
||||
|
||||
public RegionList (RegionManager rm)
|
||||
{
|
||||
_regions = new List<Region>();
|
||||
|
||||
if (!Directory.Exists(rm.GetRegionPath())) {
|
||||
throw new DirectoryNotFoundException();
|
||||
}
|
||||
|
||||
string[] files = Directory.GetFiles(rm.GetRegionPath());
|
||||
_regions.Capacity = files.Length;
|
||||
|
||||
foreach (string file in files) {
|
||||
try {
|
||||
Region r = rm.GetRegion(file);
|
||||
_regions.Add(r);
|
||||
}
|
||||
catch (ArgumentException) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator () {
|
||||
return (IEnumerator)GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator<Region> IEnumerable<Region>.GetEnumerator ()
|
||||
{
|
||||
return (IEnumerator<Region>)GetEnumerator();
|
||||
}
|
||||
|
||||
public RegionEnumerator GetEnumerator ()
|
||||
{
|
||||
return new RegionEnumerator(_regions);
|
||||
}
|
||||
}
|
||||
|
||||
public class RegionEnumerator : IEnumerator<Region>
|
||||
{
|
||||
protected List<Region> _regions;
|
||||
|
||||
protected int _pos = -1;
|
||||
|
||||
public RegionEnumerator (List<Region> regs)
|
||||
{
|
||||
_regions = regs;
|
||||
}
|
||||
|
||||
public RegionEnumerator (RegionManager rm)
|
||||
{
|
||||
_regions = new List<Region>();
|
||||
|
||||
if (!Directory.Exists(rm.GetRegionPath())) {
|
||||
throw new DirectoryNotFoundException();
|
||||
}
|
||||
|
||||
string[] files = Directory.GetFiles(rm.GetRegionPath());
|
||||
_regions.Capacity = files.Length;
|
||||
|
||||
foreach (string file in files) {
|
||||
try {
|
||||
Region r = rm.GetRegion(file);
|
||||
_regions.Add(r);
|
||||
}
|
||||
catch (ArgumentException) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool MoveNext ()
|
||||
{
|
||||
_pos++;
|
||||
return (_pos < _regions.Count);
|
||||
}
|
||||
|
||||
public void Reset ()
|
||||
{
|
||||
_pos = -1;
|
||||
}
|
||||
|
||||
void IDisposable.Dispose () { }
|
||||
|
||||
object IEnumerator.Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
|
||||
Region IEnumerator<Region>.Current
|
||||
{
|
||||
get
|
||||
{
|
||||
return Current;
|
||||
}
|
||||
}
|
||||
|
||||
public Region Current
|
||||
{
|
||||
get
|
||||
{
|
||||
try {
|
||||
return _regions[_pos];
|
||||
}
|
||||
catch (IndexOutOfRangeException) {
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
442
Substrate/SubstrateCS/Source/RegionFile.cs
Normal file
442
Substrate/SubstrateCS/Source/RegionFile.cs
Normal file
|
@ -0,0 +1,442 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using Ionic.Zlib;
|
||||
using System.Collections;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
public class RegionFile : IDisposable {
|
||||
|
||||
private const int VERSION_GZIP = 1;
|
||||
private const int VERSION_DEFLATE = 2;
|
||||
|
||||
private const int SECTOR_BYTES = 4096;
|
||||
private const int SECTOR_INTS = SECTOR_BYTES / 4;
|
||||
|
||||
const int CHUNK_HEADER_SIZE = 5;
|
||||
|
||||
private static byte[] emptySector = new byte[4096];
|
||||
|
||||
private string fileName;
|
||||
private FileStream file;
|
||||
private int[] offsets;
|
||||
private int[] chunkTimestamps;
|
||||
private List<Boolean> sectorFree;
|
||||
private int sizeDelta;
|
||||
private long lastModified = 0;
|
||||
|
||||
protected bool _disposed = false;
|
||||
|
||||
public RegionFile(string path) {
|
||||
offsets = new int[SECTOR_INTS];
|
||||
chunkTimestamps = new int[SECTOR_INTS];
|
||||
|
||||
fileName = path;
|
||||
Debugln("REGION LOAD " + fileName);
|
||||
|
||||
sizeDelta = 0;
|
||||
|
||||
ReadFile();
|
||||
}
|
||||
|
||||
~RegionFile ()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
Dispose(true);
|
||||
System.GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose (bool disposing)
|
||||
{
|
||||
if (!_disposed) {
|
||||
if (disposing) {
|
||||
// Cleanup managed resources
|
||||
}
|
||||
|
||||
// Cleanup unmanaged resources
|
||||
if (file != null) {
|
||||
file.Close();
|
||||
file = null;
|
||||
}
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
|
||||
protected void ReadFile ()
|
||||
{
|
||||
// Get last udpate time
|
||||
long newModified = 0;
|
||||
try {
|
||||
if (File.Exists(fileName)) {
|
||||
newModified = Timestamp(File.GetLastWriteTime(fileName));
|
||||
}
|
||||
}
|
||||
catch (UnauthorizedAccessException e) {
|
||||
Console.WriteLine(e.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
// If it hasn't been modified, we don't need to do anything
|
||||
if (newModified == lastModified) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
file = new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
|
||||
|
||||
//using (file) {
|
||||
if (file.Length < SECTOR_BYTES) {
|
||||
byte[] int0 = BitConverter.GetBytes((int)0);
|
||||
|
||||
/* we need to write the chunk offset table */
|
||||
for (int i = 0; i < SECTOR_INTS; ++i) {
|
||||
file.Write(int0, 0, 4);
|
||||
}
|
||||
// write another sector for the timestamp info
|
||||
for (int i = 0; i < SECTOR_INTS; ++i) {
|
||||
file.Write(int0, 0, 4);
|
||||
}
|
||||
|
||||
sizeDelta += SECTOR_BYTES * 2;
|
||||
}
|
||||
|
||||
if ((file.Length & 0xfff) != 0) {
|
||||
/* the file size is not a multiple of 4KB, grow it */
|
||||
for (int i = 0; i < (file.Length & 0xfff); ++i) {
|
||||
file.WriteByte(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* set up the available sector map */
|
||||
int nSectors = (int)file.Length / SECTOR_BYTES;
|
||||
sectorFree = new List<Boolean>(nSectors);
|
||||
|
||||
for (int i = 0; i < nSectors; ++i) {
|
||||
sectorFree.Add(true);
|
||||
}
|
||||
|
||||
sectorFree[0] = false; // chunk offset table
|
||||
sectorFree[1] = false; // for the last modified info
|
||||
|
||||
file.Seek(0, SeekOrigin.Begin);
|
||||
for (int i = 0; i < SECTOR_INTS; ++i) {
|
||||
byte[] offsetBytes = new byte[4];
|
||||
file.Read(offsetBytes, 0, 4);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(offsetBytes);
|
||||
}
|
||||
int offset = BitConverter.ToInt32(offsetBytes, 0);
|
||||
|
||||
offsets[i] = offset;
|
||||
if (offset != 0 && (offset >> 8) + (offset & 0xFF) <= sectorFree.Count) {
|
||||
for (int sectorNum = 0; sectorNum < (offset & 0xFF); ++sectorNum) {
|
||||
sectorFree[(offset >> 8) + sectorNum] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < SECTOR_INTS; ++i) {
|
||||
byte[] modBytes = new byte[4];
|
||||
file.Read(modBytes, 0, 4);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(modBytes);
|
||||
}
|
||||
int lastModValue = BitConverter.ToInt32(modBytes, 0);
|
||||
|
||||
chunkTimestamps[i] = lastModValue;
|
||||
}
|
||||
//}
|
||||
}
|
||||
catch (IOException e) {
|
||||
System.Console.WriteLine(e.Message);
|
||||
System.Console.WriteLine(e.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/* the modification date of the region file when it was first opened */
|
||||
public long LastModified() {
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
/* gets how much the region file has grown since it was last checked */
|
||||
public int GetSizeDelta() {
|
||||
int ret = sizeDelta;
|
||||
sizeDelta = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// various small debug printing helpers
|
||||
private void Debug(String str) {
|
||||
// System.Consle.Write(str);
|
||||
}
|
||||
|
||||
private void Debugln(String str) {
|
||||
Debug(str + "\n");
|
||||
}
|
||||
|
||||
private void Debug(String mode, int x, int z, String str) {
|
||||
Debug("REGION " + mode + " " + fileName + "[" + x + "," + z + "] = " + str);
|
||||
}
|
||||
|
||||
private void Debug(String mode, int x, int z, int count, String str) {
|
||||
Debug("REGION " + mode + " " + fileName + "[" + x + "," + z + "] " + count + "B = " + str);
|
||||
}
|
||||
|
||||
private void Debugln(String mode, int x, int z, String str) {
|
||||
Debug(mode, x, z, str + "\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* gets an (uncompressed) stream representing the chunk data returns null if
|
||||
* the chunk is not found or an error occurs
|
||||
*/
|
||||
public Stream GetChunkDataInputStream(int x, int z) {
|
||||
if (OutOfBounds(x, z)) {
|
||||
Debugln("READ", x, z, "out of bounds");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
int offset = GetOffset(x, z);
|
||||
if (offset == 0) {
|
||||
// Debugln("READ", x, z, "miss");
|
||||
return null;
|
||||
}
|
||||
|
||||
int sectorNumber = offset >> 8;
|
||||
int numSectors = offset & 0xFF;
|
||||
|
||||
if (sectorNumber + numSectors > sectorFree.Count) {
|
||||
Debugln("READ", x, z, "invalid sector");
|
||||
return null;
|
||||
}
|
||||
|
||||
file.Seek(sectorNumber * SECTOR_BYTES, SeekOrigin.Begin);
|
||||
byte[] lengthBytes = new byte[4];
|
||||
file.Read(lengthBytes, 0, 4);
|
||||
|
||||
if (BitConverter.IsLittleEndian) {
|
||||
Array.Reverse(lengthBytes);
|
||||
}
|
||||
int length = BitConverter.ToInt32(lengthBytes, 0);
|
||||
|
||||
if (length > SECTOR_BYTES * numSectors) {
|
||||
Debugln("READ", x, z, "invalid length: " + length + " > 4096 * " + numSectors);
|
||||
return null;
|
||||
}
|
||||
|
||||
byte version = (byte)file.ReadByte();
|
||||
if (version == VERSION_GZIP) {
|
||||
byte[] data = new byte[length - 1];
|
||||
file.Read(data, 0, data.Length);
|
||||
Stream ret = new GZipStream(new MemoryStream(data), CompressionMode.Decompress);
|
||||
// Debug("READ", x, z, " = found");
|
||||
return ret;
|
||||
} else if (version == VERSION_DEFLATE) {
|
||||
byte[] data = new byte[length - 1];
|
||||
file.Read(data, 0, data.Length);
|
||||
Stream ret = new ZlibStream(new MemoryStream(data), CompressionMode.Decompress, true);
|
||||
// Debug("READ", x, z, " = found");
|
||||
return ret;
|
||||
}
|
||||
|
||||
Debugln("READ", x, z, "unknown version " + version);
|
||||
return null;
|
||||
} catch (IOException) {
|
||||
Debugln("READ", x, z, "exception");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Stream GetChunkDataOutputStream(int x, int z) {
|
||||
if (OutOfBounds(x, z)) return null;
|
||||
|
||||
return new ZlibStream(new ChunkBuffer(this, x, z), CompressionMode.Compress);
|
||||
}
|
||||
|
||||
/*
|
||||
* lets chunk writing be multithreaded by not locking the whole file as a
|
||||
* chunk is serializing -- only writes when serialization is over
|
||||
*/
|
||||
class ChunkBuffer : MemoryStream {
|
||||
private int x, z;
|
||||
private RegionFile region;
|
||||
|
||||
public ChunkBuffer(RegionFile r, int x, int z) : base(8096) {
|
||||
// super(8096); // initialize to 8KB
|
||||
this.region = r;
|
||||
this.x = x;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public override void Close() {
|
||||
region.Write(x, z, this.GetBuffer(), (int)this.Length);
|
||||
}
|
||||
}
|
||||
|
||||
/* write a chunk at (x,z) with length bytes of data to disk */
|
||||
protected void Write(int x, int z, byte[] data, int length) {
|
||||
try {
|
||||
int offset = GetOffset(x, z);
|
||||
int sectorNumber = offset >> 8;
|
||||
int sectorsAllocated = offset & 0xFF;
|
||||
int sectorsNeeded = (length + CHUNK_HEADER_SIZE) / SECTOR_BYTES + 1;
|
||||
|
||||
// maximum chunk size is 1MB
|
||||
if (sectorsNeeded >= 256) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (sectorNumber != 0 && sectorsAllocated == sectorsNeeded) {
|
||||
/* we can simply overwrite the old sectors */
|
||||
Debug("SAVE", x, z, length, "rewrite");
|
||||
Write(sectorNumber, data, length);
|
||||
} else {
|
||||
/* we need to allocate new sectors */
|
||||
|
||||
/* mark the sectors previously used for this chunk as free */
|
||||
for (int i = 0; i < sectorsAllocated; ++i) {
|
||||
sectorFree[sectorNumber + i] = true;
|
||||
}
|
||||
|
||||
/* scan for a free space large enough to store this chunk */
|
||||
int runStart = sectorFree.FindIndex(b => b == true);
|
||||
int runLength = 0;
|
||||
if (runStart != -1) {
|
||||
for (int i = runStart; i < sectorFree.Count; ++i) {
|
||||
if (runLength != 0) {
|
||||
if (sectorFree[i]) runLength++;
|
||||
else runLength = 0;
|
||||
} else if (sectorFree[i]) {
|
||||
runStart = i;
|
||||
runLength = 1;
|
||||
}
|
||||
if (runLength >= sectorsNeeded) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (runLength >= sectorsNeeded) {
|
||||
/* we found a free space large enough */
|
||||
Debug("SAVE", x, z, length, "reuse");
|
||||
sectorNumber = runStart;
|
||||
SetOffset(x, z, (sectorNumber << 8) | sectorsNeeded);
|
||||
for (int i = 0; i < sectorsNeeded; ++i) {
|
||||
sectorFree[sectorNumber + i] = false;
|
||||
}
|
||||
Write(sectorNumber, data, length);
|
||||
} else {
|
||||
/*
|
||||
* no free space large enough found -- we need to grow the
|
||||
* file
|
||||
*/
|
||||
Debug("SAVE", x, z, length, "grow");
|
||||
file.Seek(0, SeekOrigin.End);
|
||||
sectorNumber = sectorFree.Count;
|
||||
for (int i = 0; i < sectorsNeeded; ++i) {
|
||||
file.Write(emptySector, 0, emptySector.Length);
|
||||
sectorFree.Add(false);
|
||||
}
|
||||
sizeDelta += SECTOR_BYTES * sectorsNeeded;
|
||||
|
||||
Write(sectorNumber, data, length);
|
||||
SetOffset(x, z, (sectorNumber << 8) | sectorsNeeded);
|
||||
}
|
||||
}
|
||||
SetTimestamp(x, z, Timestamp());
|
||||
} catch (IOException e) {
|
||||
Console.WriteLine(e.StackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
/* write a chunk data to the region file at specified sector number */
|
||||
private void Write(int sectorNumber, byte[] data, int length) {
|
||||
Debugln(" " + sectorNumber);
|
||||
file.Seek(sectorNumber * SECTOR_BYTES, SeekOrigin.Begin);
|
||||
|
||||
byte[] bytes = BitConverter.GetBytes(length + 1);
|
||||
if (BitConverter.IsLittleEndian) {;
|
||||
Array.Reverse(bytes);
|
||||
}
|
||||
file.Write(bytes, 0, 4); // chunk length
|
||||
file.WriteByte(VERSION_DEFLATE); // chunk version number
|
||||
file.Write(data, 0, length); // chunk data
|
||||
}
|
||||
|
||||
public void DeleteChunk (int x, int z)
|
||||
{
|
||||
int offset = GetOffset(x, z);
|
||||
int sectorNumber = offset >> 8;
|
||||
int sectorsAllocated = offset & 0xFF;
|
||||
|
||||
file.Seek(sectorNumber * SECTOR_BYTES, SeekOrigin.Begin);
|
||||
for (int i = 0; i < sectorsAllocated; i++) {
|
||||
file.Write(emptySector, 0, SECTOR_BYTES);
|
||||
}
|
||||
|
||||
SetOffset(x, z, 0);
|
||||
SetTimestamp(x, z, 0);
|
||||
}
|
||||
|
||||
/* is this an invalid chunk coordinate? */
|
||||
private bool OutOfBounds (int x, int z)
|
||||
{
|
||||
return x < 0 || x >= 32 || z < 0 || z >= 32;
|
||||
}
|
||||
|
||||
private int GetOffset(int x, int z) {
|
||||
return offsets[x + z * 32];
|
||||
}
|
||||
|
||||
public bool HasChunk(int x, int z) {
|
||||
return GetOffset(x, z) != 0;
|
||||
}
|
||||
|
||||
private void SetOffset(int x, int z, int offset) {
|
||||
offsets[x + z * 32] = offset;
|
||||
file.Seek((x + z * 32) * 4, SeekOrigin.Begin);
|
||||
|
||||
byte[] bytes = BitConverter.GetBytes(offset);
|
||||
if (BitConverter.IsLittleEndian) {;
|
||||
Array.Reverse(bytes);
|
||||
}
|
||||
|
||||
file.Write(bytes, 0, 4);
|
||||
}
|
||||
|
||||
private int Timestamp () {
|
||||
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);
|
||||
return (int)((DateTime.UtcNow - epoch).Ticks / (10000L * 1000L));
|
||||
}
|
||||
|
||||
private int Timestamp (DateTime time)
|
||||
{
|
||||
DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);
|
||||
return (int)((time - epoch).Ticks / (10000L * 1000L));
|
||||
}
|
||||
|
||||
private void SetTimestamp(int x, int z, int value) {
|
||||
chunkTimestamps[x + z * 32] = value;
|
||||
file.Seek(SECTOR_BYTES + (x + z * 32) * 4, SeekOrigin.Begin);
|
||||
|
||||
byte[] bytes = BitConverter.GetBytes(value);
|
||||
if (BitConverter.IsLittleEndian) {;
|
||||
Array.Reverse(bytes);
|
||||
}
|
||||
|
||||
file.Write(bytes, 0, 4);
|
||||
}
|
||||
|
||||
public void Close() {
|
||||
file.Close();
|
||||
}
|
||||
}
|
||||
}
|
51
Substrate/SubstrateCS/Source/RegionKey.cs
Normal file
51
Substrate/SubstrateCS/Source/RegionKey.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
public struct RegionKey : IEquatable<RegionKey>
|
||||
{
|
||||
readonly int rx;
|
||||
readonly int rz;
|
||||
|
||||
public RegionKey (int _rx, int _rz)
|
||||
{
|
||||
rx = _rx;
|
||||
rz = _rz;
|
||||
}
|
||||
|
||||
public bool Equals (RegionKey ck)
|
||||
{
|
||||
return this.rx == ck.rx && this.rz == ck.rz;
|
||||
}
|
||||
|
||||
public override bool Equals (Object o)
|
||||
{
|
||||
try {
|
||||
return this == (RegionKey)o;
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override int GetHashCode ()
|
||||
{
|
||||
int hash = 23;
|
||||
hash = hash * 37 + rx;
|
||||
hash = hash * 37 + rz;
|
||||
return hash;
|
||||
}
|
||||
|
||||
public static bool operator == (RegionKey k1, RegionKey k2)
|
||||
{
|
||||
return k1.rx == k2.rx && k1.rz == k2.rz;
|
||||
}
|
||||
|
||||
public static bool operator != (RegionKey k1, RegionKey k2)
|
||||
{
|
||||
return k1.rx != k2.rx || k1.rz != k2.rz;
|
||||
}
|
||||
}
|
||||
}
|
76
Substrate/SubstrateCS/Source/RegionManager.cs
Normal file
76
Substrate/SubstrateCS/Source/RegionManager.cs
Normal file
|
@ -0,0 +1,76 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
public class RegionManager
|
||||
{
|
||||
protected string _regionPath;
|
||||
|
||||
protected Dictionary<RegionKey, Region> _cache;
|
||||
|
||||
public RegionManager (string regionDir)
|
||||
{
|
||||
_regionPath = regionDir;
|
||||
_cache = new Dictionary<RegionKey, Region>();
|
||||
}
|
||||
|
||||
public Region GetRegion (int rx, int rz)
|
||||
{
|
||||
RegionKey k = new RegionKey(rx, rz);
|
||||
Region r;
|
||||
|
||||
try {
|
||||
if (_cache.TryGetValue(k, out r) == false) {
|
||||
r = new Region(this, rx, rz);
|
||||
_cache.Add(k, r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
catch (FileNotFoundException) {
|
||||
_cache.Add(k, null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Region GetRegion (string filename)
|
||||
{
|
||||
int rx, rz;
|
||||
if (!Region.ParseFileName(filename, out rx, out rz)) {
|
||||
throw new ArgumentException("Malformed region file name: " + filename, "filename");
|
||||
}
|
||||
|
||||
return GetRegion(rx, rz);
|
||||
}
|
||||
|
||||
public string GetRegionPath ()
|
||||
{
|
||||
return _regionPath;
|
||||
}
|
||||
|
||||
public bool DeleteRegion (int rx, int rz)
|
||||
{
|
||||
Region r = GetRegion(rx, rz);
|
||||
if (r == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RegionKey k = new RegionKey(rx, rz);
|
||||
_cache.Remove(k);
|
||||
|
||||
r.Dispose();
|
||||
|
||||
try {
|
||||
File.Delete(r.GetFilePath());
|
||||
}
|
||||
catch (Exception e) {
|
||||
Console.WriteLine("NOTICE: " + e.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
631
Substrate/SubstrateCS/Source/TileEntity.cs
Normal file
631
Substrate/SubstrateCS/Source/TileEntity.cs
Normal file
|
@ -0,0 +1,631 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
using NBT;
|
||||
using Utility;
|
||||
|
||||
public class TileEntity : INBTObject<TileEntity>, ICopyable<TileEntity>
|
||||
{
|
||||
public static readonly NBTCompoundNode BaseSchema = new NBTCompoundNode("")
|
||||
{
|
||||
new NBTScalerNode("id", NBT_Type.TAG_STRING),
|
||||
new NBTScalerNode("x", NBT_Type.TAG_INT),
|
||||
new NBTScalerNode("y", NBT_Type.TAG_INT),
|
||||
new NBTScalerNode("z", NBT_Type.TAG_INT),
|
||||
};
|
||||
|
||||
private string _id;
|
||||
private int _x;
|
||||
private int _y;
|
||||
private int _z;
|
||||
|
||||
public string ID
|
||||
{
|
||||
get { return _id; }
|
||||
}
|
||||
|
||||
public int X
|
||||
{
|
||||
get { return _x; }
|
||||
set { _x = value; }
|
||||
}
|
||||
|
||||
public int Y
|
||||
{
|
||||
get { return _y; }
|
||||
set { _y = value; }
|
||||
}
|
||||
|
||||
public int Z
|
||||
{
|
||||
get { return _z; }
|
||||
set { _z = value; }
|
||||
}
|
||||
|
||||
public TileEntity (string id)
|
||||
{
|
||||
_id = id;
|
||||
}
|
||||
|
||||
public TileEntity (TileEntity te)
|
||||
{
|
||||
_id = te._id;
|
||||
_x = te._x;
|
||||
_y = te._y;
|
||||
_z = te._z;
|
||||
}
|
||||
|
||||
public bool LocatedAt (int x, int y, int z)
|
||||
{
|
||||
return _x == x && _y == y && _z == z;
|
||||
}
|
||||
|
||||
#region ICopyable<TileEntity> Members
|
||||
|
||||
public virtual TileEntity Copy ()
|
||||
{
|
||||
return new TileEntity(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INBTObject<TileEntity> Members
|
||||
|
||||
public virtual TileEntity LoadTree (NBT_Value tree)
|
||||
{
|
||||
NBT_Compound ctree = tree as NBT_Compound;
|
||||
if (ctree == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_id = ctree["id"].ToNBTString();
|
||||
_x = ctree["x"].ToNBTInt();
|
||||
_y = ctree["y"].ToNBTInt();
|
||||
_z = ctree["z"].ToNBTInt();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public virtual TileEntity LoadTreeSafe (NBT_Value tree)
|
||||
{
|
||||
if (!ValidateTree(tree)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return LoadTree(tree);
|
||||
}
|
||||
|
||||
public virtual NBT_Value BuildTree ()
|
||||
{
|
||||
NBT_Compound tree = new NBT_Compound();
|
||||
tree["id"] = new NBT_String(_id);
|
||||
tree["x"] = new NBT_Int(_x);
|
||||
tree["y"] = new NBT_Int(_y);
|
||||
tree["z"] = new NBT_Int(_z);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
public virtual bool ValidateTree (NBT_Value tree)
|
||||
{
|
||||
return new NBTVerifier(tree, BaseSchema).Verify();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class TileEntityFurnace : TileEntity, IItemContainer
|
||||
{
|
||||
public static readonly NBTCompoundNode FurnaceSchema = BaseSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Furnace"),
|
||||
new NBTScalerNode("BurnTime", NBT_Type.TAG_SHORT),
|
||||
new NBTScalerNode("CookTime", NBT_Type.TAG_SHORT),
|
||||
new NBTListNode("Items", NBT_Type.TAG_COMPOUND, ItemCollection.ListSchema),
|
||||
});
|
||||
|
||||
private const int _CAPACITY = 3;
|
||||
|
||||
private short _burnTime;
|
||||
private short _cookTime;
|
||||
|
||||
private ItemCollection _items;
|
||||
|
||||
public int BurnTime
|
||||
{
|
||||
get { return _burnTime; }
|
||||
set { _burnTime = (short)value; }
|
||||
}
|
||||
|
||||
public int CookTime
|
||||
{
|
||||
get { return _cookTime; }
|
||||
set { _cookTime = (short)value; }
|
||||
}
|
||||
|
||||
public TileEntityFurnace ()
|
||||
: base("Furnace")
|
||||
{
|
||||
_items = new ItemCollection(_CAPACITY);
|
||||
}
|
||||
|
||||
public TileEntityFurnace (TileEntity te)
|
||||
: base (te)
|
||||
{
|
||||
TileEntityFurnace tec = te as TileEntityFurnace;
|
||||
if (tec != null) {
|
||||
_cookTime = tec._cookTime;
|
||||
_burnTime = tec._burnTime;
|
||||
_items = tec._items.Copy();
|
||||
}
|
||||
else {
|
||||
_items = new ItemCollection(_CAPACITY);
|
||||
}
|
||||
}
|
||||
|
||||
#region ICopyable<TileEntity> Members
|
||||
|
||||
public override TileEntity Copy ()
|
||||
{
|
||||
return new TileEntityFurnace(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IItemContainer Members
|
||||
|
||||
public ItemCollection Items
|
||||
{
|
||||
get { return _items; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INBTObject<TileEntity> Members
|
||||
|
||||
public override TileEntity LoadTree (NBT_Value tree)
|
||||
{
|
||||
NBT_Compound ctree = tree as NBT_Compound;
|
||||
if (ctree == null || base.LoadTree(tree) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_burnTime = ctree["BurnTime"].ToNBTShort();
|
||||
_cookTime = ctree["CookTime"].ToNBTShort();
|
||||
|
||||
NBT_List items = ctree["Items"].ToNBTList();
|
||||
_items = new ItemCollection(_CAPACITY).LoadTree(items);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public override NBT_Value BuildTree ()
|
||||
{
|
||||
NBT_Compound tree = base.BuildTree() as NBT_Compound;
|
||||
tree["BurnTime"] = new NBT_Short(_burnTime);
|
||||
tree["CookTime"] = new NBT_Short(_cookTime);
|
||||
tree["Items"] = _items.BuildTree();
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
public override bool ValidateTree (NBT_Value tree)
|
||||
{
|
||||
return new NBTVerifier(tree, FurnaceSchema).Verify();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class TileEntitySign : TileEntity
|
||||
{
|
||||
public static readonly NBTCompoundNode SignSchema = BaseSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Sign"),
|
||||
new NBTScalerNode("Text1", NBT_Type.TAG_STRING),
|
||||
new NBTScalerNode("Text2", NBT_Type.TAG_STRING),
|
||||
new NBTScalerNode("Text3", NBT_Type.TAG_STRING),
|
||||
new NBTScalerNode("Text4", NBT_Type.TAG_STRING),
|
||||
});
|
||||
|
||||
private string _text1 = "";
|
||||
private string _text2 = "";
|
||||
private string _text3 = "";
|
||||
private string _text4 = "";
|
||||
|
||||
public string Text1
|
||||
{
|
||||
get { return _text1; }
|
||||
set { _text1 = value.Substring(0, 12); }
|
||||
}
|
||||
|
||||
public string Text2
|
||||
{
|
||||
get { return _text2; }
|
||||
set { _text2 = value.Substring(0, 12); }
|
||||
}
|
||||
|
||||
public string Text3
|
||||
{
|
||||
get { return _text3; }
|
||||
set { _text3 = value.Substring(0, 12); }
|
||||
}
|
||||
|
||||
public string Text4
|
||||
{
|
||||
get { return _text4; }
|
||||
set { _text4 = value.Substring(0, 12); }
|
||||
}
|
||||
|
||||
public TileEntitySign ()
|
||||
: base("Sign")
|
||||
{
|
||||
}
|
||||
|
||||
public TileEntitySign (TileEntity te)
|
||||
: base(te)
|
||||
{
|
||||
TileEntitySign tes = te as TileEntitySign;
|
||||
if (tes != null) {
|
||||
_text1 = tes._text1;
|
||||
_text2 = tes._text2;
|
||||
_text3 = tes._text3;
|
||||
_text4 = tes._text4;
|
||||
}
|
||||
}
|
||||
|
||||
#region ICopyable<TileEntity> Members
|
||||
|
||||
public override TileEntity Copy ()
|
||||
{
|
||||
return new TileEntitySign(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INBTObject<TileEntity> Members
|
||||
|
||||
public override TileEntity LoadTree (NBT_Value tree)
|
||||
{
|
||||
NBT_Compound ctree = tree as NBT_Compound;
|
||||
if (ctree == null || base.LoadTree(tree) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_text1 = ctree["Text1"].ToNBTString();
|
||||
_text2 = ctree["Text2"].ToNBTString();
|
||||
_text3 = ctree["Text3"].ToNBTString();
|
||||
_text4 = ctree["Text4"].ToNBTString();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public override NBT_Value BuildTree ()
|
||||
{
|
||||
NBT_Compound tree = base.BuildTree() as NBT_Compound;
|
||||
tree["Text1"] = new NBT_String(_text1);
|
||||
tree["Text2"] = new NBT_String(_text2);
|
||||
tree["Text3"] = new NBT_String(_text3);
|
||||
tree["Text4"] = new NBT_String(_text4);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
public override bool ValidateTree (NBT_Value tree)
|
||||
{
|
||||
return new NBTVerifier(tree, SignSchema).Verify();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class TileEntityMobSpawner : TileEntity
|
||||
{
|
||||
public static readonly NBTCompoundNode MobSpawnerSchema = BaseSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "MobSpawner"),
|
||||
new NBTScalerNode("EntityId", NBT_Type.TAG_STRING),
|
||||
new NBTScalerNode("Delay", NBT_Type.TAG_SHORT),
|
||||
});
|
||||
|
||||
private short _delay;
|
||||
private string _entityID;
|
||||
|
||||
public int Delay
|
||||
{
|
||||
get { return _delay; }
|
||||
set { _delay = (short)value; }
|
||||
}
|
||||
|
||||
public string EntityID
|
||||
{
|
||||
get { return _entityID; }
|
||||
set { _entityID = value; }
|
||||
}
|
||||
|
||||
public TileEntityMobSpawner ()
|
||||
: base("MobSpawner")
|
||||
{
|
||||
}
|
||||
|
||||
public TileEntityMobSpawner (TileEntity te)
|
||||
: base(te)
|
||||
{
|
||||
TileEntityMobSpawner tes = te as TileEntityMobSpawner;
|
||||
if (tes != null) {
|
||||
_delay = tes._delay;
|
||||
_entityID = tes._entityID;
|
||||
}
|
||||
}
|
||||
|
||||
#region ICopyable<TileEntity> Members
|
||||
|
||||
public override TileEntity Copy ()
|
||||
{
|
||||
return new TileEntityMobSpawner(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INBTObject<TileEntity> Members
|
||||
|
||||
public override TileEntity LoadTree (NBT_Value tree)
|
||||
{
|
||||
NBT_Compound ctree = tree as NBT_Compound;
|
||||
if (ctree == null || base.LoadTree(tree) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_delay = ctree["Delay"].ToNBTShort();
|
||||
_entityID = ctree["EntityID"].ToNBTString();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public override NBT_Value BuildTree ()
|
||||
{
|
||||
NBT_Compound tree = base.BuildTree() as NBT_Compound;
|
||||
tree["EntityID"] = new NBT_String(_entityID);
|
||||
tree["Delay"] = new NBT_Short(_delay);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
public override bool ValidateTree (NBT_Value tree)
|
||||
{
|
||||
return new NBTVerifier(tree, MobSpawnerSchema).Verify();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class TileEntityChest : TileEntity, IItemContainer
|
||||
{
|
||||
public static readonly NBTCompoundNode ChestSchema = BaseSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Chest"),
|
||||
new NBTListNode("Items", NBT_Type.TAG_COMPOUND, ItemCollection.ListSchema),
|
||||
});
|
||||
|
||||
private const int _CAPACITY = 27;
|
||||
|
||||
private ItemCollection _items;
|
||||
|
||||
public TileEntityChest ()
|
||||
: base("Chest")
|
||||
{
|
||||
_items = new ItemCollection(_CAPACITY);
|
||||
}
|
||||
|
||||
public TileEntityChest (TileEntity te)
|
||||
: base(te)
|
||||
{
|
||||
TileEntityChest tec = te as TileEntityChest;
|
||||
if (tec != null) {
|
||||
_items = tec._items.Copy();
|
||||
}
|
||||
else {
|
||||
_items = new ItemCollection(_CAPACITY);
|
||||
}
|
||||
}
|
||||
|
||||
#region ICopyable<TileEntity> Members
|
||||
|
||||
public override TileEntity Copy ()
|
||||
{
|
||||
return new TileEntityChest(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IItemContainer Members
|
||||
|
||||
public ItemCollection Items
|
||||
{
|
||||
get { return _items; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INBTObject<TileEntity> Members
|
||||
|
||||
public override TileEntity LoadTree (NBT_Value tree)
|
||||
{
|
||||
NBT_Compound ctree = tree as NBT_Compound;
|
||||
if (ctree == null || base.LoadTree(tree) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
NBT_List items = ctree["Items"].ToNBTList();
|
||||
_items = new ItemCollection(_CAPACITY).LoadTree(items);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public override NBT_Value BuildTree ()
|
||||
{
|
||||
NBT_Compound tree = base.BuildTree() as NBT_Compound;
|
||||
tree["Items"] = _items.BuildTree();
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
public override bool ValidateTree (NBT_Value tree)
|
||||
{
|
||||
return new NBTVerifier(tree, ChestSchema).Verify();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class TileEntityMusic : TileEntity
|
||||
{
|
||||
public static readonly NBTCompoundNode MusicSchema = BaseSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Music"),
|
||||
new NBTScalerNode("note", NBT_Type.TAG_BYTE),
|
||||
});
|
||||
|
||||
private byte _note;
|
||||
|
||||
public int Note
|
||||
{
|
||||
get { return _note; }
|
||||
set { _note = (byte)value; }
|
||||
}
|
||||
|
||||
public TileEntityMusic ()
|
||||
: base("Music")
|
||||
{
|
||||
}
|
||||
|
||||
public TileEntityMusic (TileEntity te)
|
||||
: base(te)
|
||||
{
|
||||
TileEntityMusic tes = te as TileEntityMusic;
|
||||
if (tes != null) {
|
||||
_note = tes._note;
|
||||
}
|
||||
}
|
||||
|
||||
#region ICopyable<TileEntity> Members
|
||||
|
||||
public override TileEntity Copy ()
|
||||
{
|
||||
return new TileEntityMusic(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INBTObject<TileEntity> Members
|
||||
|
||||
public override TileEntity LoadTree (NBT_Value tree)
|
||||
{
|
||||
NBT_Compound ctree = tree as NBT_Compound;
|
||||
if (ctree == null || base.LoadTree(tree) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
_note = ctree["Note"].ToNBTByte();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public override NBT_Value BuildTree ()
|
||||
{
|
||||
NBT_Compound tree = base.BuildTree() as NBT_Compound;
|
||||
tree["Note"] = new NBT_Byte(_note);
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
public override bool ValidateTree (NBT_Value tree)
|
||||
{
|
||||
return new NBTVerifier(tree, MusicSchema).Verify();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class TileEntityTrap : TileEntity, IItemContainer
|
||||
{
|
||||
public static readonly NBTCompoundNode TrapSchema = BaseSchema.MergeInto(new NBTCompoundNode("")
|
||||
{
|
||||
new NBTStringNode("id", "Trap"),
|
||||
new NBTListNode("Items", NBT_Type.TAG_COMPOUND, ItemCollection.ListSchema),
|
||||
});
|
||||
|
||||
private const int _CAPACITY = 8;
|
||||
|
||||
private ItemCollection _items;
|
||||
|
||||
public TileEntityTrap ()
|
||||
: base("Trap")
|
||||
{
|
||||
_items = new ItemCollection(_CAPACITY);
|
||||
}
|
||||
|
||||
public TileEntityTrap (TileEntity te)
|
||||
: base(te)
|
||||
{
|
||||
TileEntityTrap tec = te as TileEntityTrap;
|
||||
if (tec != null) {
|
||||
_items = tec._items.Copy();
|
||||
}
|
||||
else {
|
||||
_items = new ItemCollection(_CAPACITY);
|
||||
}
|
||||
}
|
||||
|
||||
#region ICopyable<TileEntity> Members
|
||||
|
||||
public override TileEntity Copy ()
|
||||
{
|
||||
return new TileEntityTrap(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IItemContainer Members
|
||||
|
||||
public ItemCollection Items
|
||||
{
|
||||
get { return _items; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region INBTObject<TileEntity> Members
|
||||
|
||||
public override TileEntity LoadTree (NBT_Value tree)
|
||||
{
|
||||
NBT_Compound ctree = tree as NBT_Compound;
|
||||
if (ctree == null || base.LoadTree(tree) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
NBT_List items = ctree["Items"].ToNBTList();
|
||||
_items = new ItemCollection(_CAPACITY).LoadTree(items);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public override NBT_Value BuildTree ()
|
||||
{
|
||||
NBT_Compound tree = base.BuildTree() as NBT_Compound;
|
||||
tree["Items"] = _items.BuildTree();
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
public override bool ValidateTree (NBT_Value tree)
|
||||
{
|
||||
return new NBTVerifier(tree, TrapSchema).Verify();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
64
Substrate/SubstrateCS/Source/TileEntityFactory.cs
Normal file
64
Substrate/SubstrateCS/Source/TileEntityFactory.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
using NBT;
|
||||
|
||||
public class TileEntityFactory
|
||||
{
|
||||
private static Dictionary<string, Type> _registry;
|
||||
|
||||
public static TileEntity Create (string type)
|
||||
{
|
||||
Type t;
|
||||
if (!_registry.TryGetValue(type, out t)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Activator.CreateInstance(t) as TileEntity;
|
||||
}
|
||||
|
||||
public static TileEntity Create (NBT_Compound tree)
|
||||
{
|
||||
string type = tree["id"].ToNBTString();
|
||||
|
||||
Type t;
|
||||
if (!_registry.TryGetValue(type, out t)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
TileEntity te = Activator.CreateInstance(t, new object[] { tree }) as TileEntity;
|
||||
|
||||
return te.LoadTreeSafe(tree);
|
||||
}
|
||||
|
||||
public static Type Lookup (string type)
|
||||
{
|
||||
Type t;
|
||||
if (!_registry.TryGetValue(type, out t)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
public static void Register (string id, Type subtype)
|
||||
{
|
||||
_registry[id] = subtype;
|
||||
}
|
||||
|
||||
static TileEntityFactory ()
|
||||
{
|
||||
_registry = new Dictionary<string, Type>();
|
||||
|
||||
_registry["Chest"] = typeof(TileEntityChest);
|
||||
_registry["Furnace"] = typeof(TileEntityFurnace);
|
||||
_registry["MobSpawner"] = typeof(TileEntityMobSpawner);
|
||||
_registry["Music"] = typeof(TileEntityMusic);
|
||||
_registry["Sign"] = typeof(TileEntitySign);
|
||||
_registry["Trap"] = typeof(TileEntityTrap);
|
||||
}
|
||||
}
|
||||
}
|
11
Substrate/SubstrateCS/Source/Utility/Interface.cs
Normal file
11
Substrate/SubstrateCS/Source/Utility/Interface.cs
Normal file
|
@ -0,0 +1,11 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map.Utility
|
||||
{
|
||||
public interface ICopyable <T>
|
||||
{
|
||||
T Copy ();
|
||||
}
|
||||
}
|
62
Substrate/SubstrateCS/Source/Utility/NibbleArray.cs
Normal file
62
Substrate/SubstrateCS/Source/Utility/NibbleArray.cs
Normal file
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace NBToolkit.Map.Utility
|
||||
{
|
||||
public class NibbleArray : ICopyable<NibbleArray>
|
||||
{
|
||||
protected readonly byte[] _data = null;
|
||||
|
||||
public NibbleArray (byte[] data)
|
||||
{
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public int this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
int subs = index >> 1;
|
||||
if ((index & 1) == 0) {
|
||||
return _data[subs] & 0x0F;
|
||||
}
|
||||
else {
|
||||
return (_data[subs] >> 4) & 0x0F;
|
||||
}
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
int subs = index >> 1;
|
||||
if ((index & 1) == 0) {
|
||||
_data[subs] = (byte)((_data[subs] & 0xF0) | (value & 0x0F));
|
||||
}
|
||||
else {
|
||||
_data[subs] = (byte)((_data[subs] & 0x0F) | ((value & 0x0F) << 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _data.Length << 1;
|
||||
}
|
||||
}
|
||||
|
||||
#region ICopyable<NibbleArray> Members
|
||||
|
||||
public NibbleArray Copy ()
|
||||
{
|
||||
byte[] data = new byte[_data.Length];
|
||||
_data.CopyTo(data, 0);
|
||||
|
||||
return new NibbleArray(data);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
40
Substrate/SubstrateCS/Source/World.cs
Normal file
40
Substrate/SubstrateCS/Source/World.cs
Normal file
|
@ -0,0 +1,40 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
|
||||
namespace NBToolkit.Map
|
||||
{
|
||||
public class World
|
||||
{
|
||||
protected RegionManager _regionMan;
|
||||
protected ChunkManager _chunkMan;
|
||||
protected BlockManager _blockMan;
|
||||
|
||||
protected string _worldPath;
|
||||
|
||||
public World (string world)
|
||||
{
|
||||
_worldPath = world;
|
||||
|
||||
_regionMan = new RegionManager(Path.Combine(world, "region"));
|
||||
_chunkMan = new ChunkManager(_regionMan);
|
||||
_blockMan = new BlockManager(_chunkMan);
|
||||
}
|
||||
|
||||
public RegionManager GetRegionManager ()
|
||||
{
|
||||
return _regionMan;
|
||||
}
|
||||
|
||||
public ChunkManager GetChunkManager ()
|
||||
{
|
||||
return _chunkMan;
|
||||
}
|
||||
|
||||
public BlockManager GetBlockManager ()
|
||||
{
|
||||
return _blockMan;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue