diff --git a/Substrate/SubstrateCS/Source/AlphaBlockCollection.cs b/Substrate/SubstrateCS/Source/AlphaBlockCollection.cs index f2b6120..884ce1d 100644 --- a/Substrate/SubstrateCS/Source/AlphaBlockCollection.cs +++ b/Substrate/SubstrateCS/Source/AlphaBlockCollection.cs @@ -6,47 +6,13 @@ using Substrate.NBT; namespace Substrate { - public struct BlockData { - public XZYByteArray blocks; - - public BlockData (XZYByteArray b) - { - blocks = b; - } - } - - public struct BlockDataData { - public XZYNibbleArray data; - - public BlockDataData (XZYNibbleArray d) - { - data = d; - } - } - - public struct BlockLightData { - public XZYNibbleArray light; - public XZYNibbleArray skyLight; - public ZXByteArray heightMap; - - public BlockLightData (XZYNibbleArray l, XZYNibbleArray s, ZXByteArray h) - { - light = l; - skyLight = s; - heightMap = h; - } - } - - public struct BlockPropertyData { - public TagNodeList tileEntities; - - public BlockPropertyData (TagNodeList t) - { - tileEntities = t; - } - } - - public class AlphaBlockCollection : IBoundedAlphaBlockCollection, IEnumerable + /// + /// Functions for reading and modifying a bounded-size collection of Alpha-compatible block data. + /// + /// An is a wrapper around existing pieces of data. Although it + /// holds references to data, it does not "own" the data in the same way that a does. An + /// simply overlays a higher-level interface on top of existing data. + public class AlphaBlockCollection : IBoundedAlphaBlockCollection { private readonly int _xdim; private readonly int _ydim; @@ -96,32 +62,62 @@ namespace Substrate remove { _tileEntityManager.TranslateCoordinates -= value; } } + /// + /// Gets or sets a value indicating whether changes to blocks will trigger automatic lighting updates. + /// + /// Automatic updates to lighting may spill into neighboring objects, if they can + /// be resolved. public bool AutoLight { get { return _autoLight; } set { _autoLight = value; } } + /// + /// Gets or sets a value indicating whether changes to blocks will trigger automatic fluid updates. + /// + /// Automatic updates to fluid may cascade through neighboring objects and beyond, + /// if they can be resolved. public bool AutoFluid { get { return _autoFluid; } set { _autoFluid = value; } } + /// + /// Gets or sets a value indicating whether this needs to be saved. + /// + /// If this is backed by a reference conainer type, set this property to false + /// to prevent any modifications from being saved. The next update will set this property true again, however. public bool IsDirty { get { return _dirty; } set { _dirty = value; } } - public AlphaBlockCollection (BlockData root, BlockDataData data, BlockLightData light, BlockPropertyData properties) + /// + /// Creates a new overlay on top of Alpha-specific units of data. + /// + /// An array of Block IDs. + /// An array of data nibbles. + /// An array of block light nibbles. + /// An array of sky light nibbles. + /// An array of height map values. + /// A list of tile entities corresponding to blocks in this collection. + public AlphaBlockCollection ( + XZYByteArray blocks, + XZYNibbleArray data, + XZYNibbleArray blockLight, + XZYNibbleArray skyLight, + ZXByteArray heightMap, + TagNodeList tileEntities) { - _blocks = root.blocks; - _data = data.data; - _blockLight = light.light; - _skyLight = light.skyLight; - _heightMap = light.heightMap; - _tileEntities = properties.tileEntities; + _blocks = blocks; + _data = data; + _blockLight = blockLight; + _skyLight = skyLight; + _heightMap = heightMap; + _tileEntities = tileEntities; _xdim = _blocks.XDim; _ydim = _blocks.YDim; @@ -132,16 +128,42 @@ namespace Substrate _tileEntityManager = new BlockTileEntities(_blocks, _tileEntities); } + /// + /// Returns a new object from local coordinates relative to this collection. + /// + /// Local X-coordinate of block. + /// Local Y-coordinate of block. + /// Local Z-coordiante of block. + /// A new object representing context-independent data of a single block. + /// Context-independent data excludes data such as lighting. object actually contain a copy + /// of the data they represent, so changes to the will not affect this container, and vice-versa. public Block GetBlock (int x, int y, int z) { return new Block(this, x, y, z); } + /// + /// Returns a new object from local coordaintes relative to this collection. + /// + /// Local X-coordinate of block. + /// Local Y-coordinate of block. + /// Local Z-coordinate of block. + /// A new object representing context-dependent data of a single block. + /// Context-depdendent data includes all data associated with this block. Since a represents + /// a view of a block within this container, any updates to data in the container will be reflected in the , + /// and vice-versa for updates to the . public BlockRef GetBlockRef (int x, int y, int z) { return new BlockRef(this, x, y, z); } + /// + /// Updates a block in this collection with values from a object. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// A object to copy block data from. public void SetBlock (int x, int y, int z, Block block) { SetID(x, y, z, block.ID); @@ -152,36 +174,75 @@ namespace Substrate #region IBlockCollection Members + /// + /// Gets the length of this collection's X-dimension. + /// public int XDim { get { return _xdim; } } + /// + /// Gets the length of this collection's Y-dimension. + /// public int YDim { get { return _ydim; } } + /// + /// Gets the length of this collection's Z-dimension. + /// public int ZDim { get { return _zdim; } } + /// + /// Returns an object compatible with the interface from local coordinates relative to this collection. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// An -compatible object. + /// IBlock IBlockCollection.GetBlock (int x, int y, int z) { return GetBlock(x, y, z); } + /// + /// Returns a reference object compatible with the interface from local coordinates relative to this collection. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// An -compatible reference object. + /// IBlock IBlockCollection.GetBlockRef (int x, int y, int z) { return GetBlockRef(x, y, z); } + /// + /// Updates a block in this collection with values from an object. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// An object to copy block data from. public void SetBlock (int x, int y, int z, IBlock block) { SetID(x, y, z, block.ID); } + /// + /// Gets information on the type of a block at the given local coordinates. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// A object containing information of a block's type. public BlockInfo GetInfo (int x, int y, int z) { return BlockInfo.BlockTable[_blocks[x, y, z]]; @@ -192,6 +253,13 @@ namespace Substrate return BlockInfo.BlockTable[_blocks[index]]; } + /// + /// Gets the ID (type) of a block at the given local coordinates. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// The ID (type) of the specified block. public int GetID (int x, int y, int z) { return _blocks[x, y, z]; @@ -202,6 +270,18 @@ namespace Substrate return _blocks[index]; } + /// + /// Sets the ID (type) of a block at the given local coordinates, maintaining consistency of data. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// The new ID of the block. + /// Depending on the options set for this , this method can be very + /// heavy-handed in the amount of work it does to maintain consistency of tile entities, lighting, fluid, etc. + /// for the affected block and possibly many other indirectly-affected blocks in the collection or neighboring + /// collections. If many SetID calls are expected to be made, some of this auto-reconciliation behavior should + /// be disabled, and the data should be rebuilt at the -level at the end. public void SetID (int x, int y, int z, int id) { int oldid = _blocks[x, y, z]; @@ -258,6 +338,24 @@ namespace Substrate _dirty = true; } + internal void SetID (int index, int id) + { + int yzdim = _ydim * _zdim; + + int x = index / yzdim; + int zy = index - (x * yzdim); + + int z = zy / _ydim; + int y = zy - (z * _ydim); + + SetID(x, y, z, id); + } + + /// + /// Returns a count of all blocks in this with the given ID (type). + /// + /// The ID of blocks to count. + /// A count of all matching blocks. public int CountByID (int id) { int c = 0; @@ -275,22 +373,52 @@ namespace Substrate #region IDataBlockContainer Members + /// + /// Returns an object compatible with the interface from local coordinates relative to this collection. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// An -compatible object. + /// IDataBlock IDataBlockCollection.GetBlock (int x, int y, int z) { return GetBlock(x, y, z); } + /// + /// Returns a reference object compatible with the interface from local coordinates relative to this collection. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// An -compatible reference object. + /// IDataBlock IDataBlockCollection.GetBlockRef (int x, int y, int z) { return GetBlockRef(x, y, z); } + /// + /// Updates a block in this collection with values from an object. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// An object to copy block data from. public void SetBlock (int x, int y, int z, IDataBlock block) { SetID(x, y, z, block.ID); SetData(x, y, z, block.Data); } + /// + /// Gets the data value of a block at the given local coordinates. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// The data value of the specified block. public int GetData (int x, int y, int z) { return _data[x, y, z]; @@ -301,6 +429,13 @@ namespace Substrate return _data[index]; } + /// + /// Sets the data value of a block at the given local coordinates. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// The new data value of the block. public void SetData (int x, int y, int z, int data) { if (_data[x, y, z] != data) { @@ -323,6 +458,12 @@ namespace Substrate } } + /// + /// Returns a count of all blocks in this matching the given ID (type) and data value. + /// + /// The ID of blocks to count. + /// The data value of blocks to count. + /// A count of all matching blocks. public int CountByData (int id, int data) { int c = 0; @@ -340,16 +481,39 @@ namespace Substrate #region ILitBlockCollection Members + /// + /// Not Implemented. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// An -compatible object. + /// ILitBlock ILitBlockCollection.GetBlock (int x, int y, int z) { throw new NotImplementedException(); } + /// + /// Returns a reference object compatible with the interface from local coordinates relative to this collection. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// An -compatible reference object. + /// ILitBlock ILitBlockCollection.GetBlockRef (int x, int y, int z) { return GetBlockRef(x, y, z); } + /// + /// Updates a block in this collection with values from an object. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// An object to copy block data from. public void SetBlock (int x, int y, int z, ILitBlock block) { SetID(x, y, z, block.ID); @@ -357,6 +521,13 @@ namespace Substrate SetSkyLight(x, y, z, block.SkyLight); } + /// + /// Gets the block-source light value of a block at the given local coordinates. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// The block-source light value of the specified block. public int GetBlockLight (int x, int y, int z) { return _blockLight[x, y, z]; @@ -367,6 +538,13 @@ namespace Substrate return _blockLight[index]; } + /// + /// Gets the sky-source light value of a block at the given local coordinates. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// The sky-source light value of the specified block. public int GetSkyLight (int x, int y, int z) { return _skyLight[x, y, z]; @@ -377,6 +555,13 @@ namespace Substrate return _skyLight[index]; } + /// + /// Sets the blocks-source light value of a block at the given local coordinates. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// The new block-source light value of the block. public void SetBlockLight (int x, int y, int z, int light) { if (_blockLight[x, y, z] != light) { @@ -393,6 +578,13 @@ namespace Substrate } } + /// + /// Sets the sky-source light value of a block at the given local coordinates. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// The new sky-source light value of the block. public void SetSkyLight (int x, int y, int z, int light) { if (_skyLight[x, y, z] != light) { @@ -409,76 +601,173 @@ namespace Substrate } } + /// + /// Gets the lowest Y-coordinate of a block at which sky-source light remains unfiltered. + /// + /// Local X-coordinate of a map location. + /// Local Z-coordinate of a map location. + /// The height-map value of a map location for calculating sky-source lighting. public int GetHeight (int x, int z) { return _heightMap[x, z]; } + /// + /// Sets the lowest Y-coordinate of a block at which sky-source light remains unfiltered. + /// + /// Local X-coordinate of a map location. + /// Local Z-coordinate of a map location. + /// The new height-map value of the given map location. public void SetHeight (int x, int z, int height) { _heightMap[x, z] = (byte)height; } + /// + /// Updates the block-source lighting of a block at the given local coordinates to maintain light consistency. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// The lighting of the block will be updated to be consistent with the lighting in neighboring blocks. + /// If the block is itself a light source, many nearby blocks may be updated to maintain consistent lighting. These + /// updates may also touch neighboring objects, if they can be resolved. + /// This function assumes that the entire and neighboring s + /// already have consistent lighting, with the exception of the block being updated. If this assumption is violated, + /// lighting may fail to converge correctly. public void UpdateBlockLight (int x, int y, int z) { _lightManager.UpdateBlockLight(x, y, z); _dirty = true; } + /// + /// Updates the sky-source lighting of a block at the given local coordinates to maintain light consistency. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// The lighting of the block will be updated to be consistent with the lighting in neighboring blocks. + /// If the block is itself a light source, many nearby blocks may be updated to maintain consistent lighting. These + /// updates may also touch neighboring objects, if they can be resolved. + /// This function assumes that the entire and neighboring s + /// already have consistent lighting, with the exception of the block being updated. If this assumption is violated, + /// lighting may fail to converge correctly. public void UpdateSkyLight (int x, int y, int z) { _lightManager.UpdateBlockSkyLight(x, y, z); _dirty = true; } + /// + /// Resets the block-source light value to 0 for all blocks in this . + /// public void ResetBlockLight () { _blockLight.Clear(); _dirty = true; } + /// + /// Resets the sky-source light value to 0 for all blocks in this . + /// public void ResetSkyLight () { _skyLight.Clear(); _dirty = true; } + /// + /// Reconstructs the block-source lighting for all blocks in this . + /// + /// This function should only be called after the lighting has been reset in this + /// and all neighboring s, or lighting may fail to converge correctly. + /// This function cannot reset the lighting on its own, due to interactions between s. + /// If many light source or block opacity values will be modified in this , it may + /// be preferable to avoid explicit or implicit calls to and call this function once when + /// modifications are complete. + /// / public void RebuildBlockLight () { _lightManager.RebuildBlockLight(); _dirty = true; } + /// + /// Reconstructs the sky-source lighting for all blocks in this . + /// + /// This function should only be called after the lighting has been reset in this + /// and all neighboring s, or lighting may fail to converge correctly. + /// This function cannot reset the lighting on its own, due to interactions between s. + /// If many light source or block opacity values will be modified in this , it may + /// be preferable to avoid explicit or implicit calls to and call this function once when + /// modifications are complete. + /// public void RebuildSkyLight () { _lightManager.RebuildBlockSkyLight(); _dirty = true; } + /// + /// Reconstructs the height-map for this . + /// public void RebuildHeightMap () { _lightManager.RebuildHeightMap(); _dirty = true; } + /// + /// Reconciles any block-source lighting inconsistencies between this and any of its neighbors. + /// + /// It will be necessary to call this function if an is reset and rebuilt, but + /// some of its neighbors are not. A rebuilt will spill lighting updates into its neighbors, + /// but will not see lighting that should be propagated back from its neighbors. + /// public void StitchBlockLight () { _lightManager.StitchBlockLight(); _dirty = true; } + /// + /// Reconciles any sky-source lighting inconsistencies between this and any of its neighbors. + /// + /// It will be necessary to call this function if an is reset and rebuilt, but + /// some of its neighbors are not. A rebuilt will spill lighting updates into its neighbors, + /// but will not see lighting that should be propagated back from its neighbors. + /// public void StitchSkyLight () { _lightManager.StitchBlockSkyLight(); _dirty = true; } + /// + /// Reconciles any block-source lighting inconsistencies between this and another on a given edge. + /// + /// An -compatible object with the same dimensions as this . + /// The edge that is a neighbor on. + /// It will be necessary to call this function if an is reset and rebuilt, but + /// some of its neighbors are not. A rebuilt will spill lighting updates into its neighbors, + /// but will not see lighting that should be propagated back from its neighbors. + /// public void StitchBlockLight (IBoundedLitBlockCollection blockset, BlockCollectionEdge edge) { _lightManager.StitchBlockLight(blockset, edge); _dirty = true; } + /// + /// Reconciles any sky-source lighting inconsistencies between this and another on a given edge. + /// + /// An -compatible object with the same dimensions as this . + /// The edge that is a neighbor on. + /// It will be necessary to call this function if an is reset and rebuilt, but + /// some of its neighbors are not. A rebuilt will spill lighting updates into its neighbors, + /// but will not see lighting that should be propagated back from its neighbors. + /// public void StitchSkyLight (IBoundedLitBlockCollection blockset, BlockCollectionEdge edge) { _lightManager.StitchBlockSkyLight(blockset, edge); @@ -490,39 +779,92 @@ namespace Substrate #region IPropertyBlockCollection Members + /// + /// Returns an object compatible with the interface from local coordinates relative to this collection. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// An -compatible object. + /// IPropertyBlock IPropertyBlockCollection.GetBlock (int x, int y, int z) { return GetBlock(x, y, z); } + /// + /// Returns a reference object compatible with the interface from local coordinates relative to this collection. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// An -compatible reference object. + /// IPropertyBlock IPropertyBlockCollection.GetBlockRef (int x, int y, int z) { return GetBlockRef(x, y, z); } + /// + /// Updates a block in this collection with values from an object. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// An object to copy block data from. public void SetBlock (int x, int y, int z, IPropertyBlock block) { SetID(x, y, z, block.ID); SetTileEntity(x, y, z, block.GetTileEntity().Copy()); } + /// + /// Gets a record for a block at the given local coordinates, if one exists. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// A for the given block, or null if the is missing or the block type does not use a . public TileEntity GetTileEntity (int x, int y, int z) { return _tileEntityManager.GetTileEntity(x, y, z); } + /// + /// Sets a record for a block at the given local coordinates. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// The to add to the given block. + /// Thrown when the being passed is of the wrong type for the given block. + /// Thrown when the given block is of a type that does not support a record. public void SetTileEntity (int x, int y, int z, TileEntity te) { _tileEntityManager.SetTileEntity(x, y, z, te); _dirty = true; } + /// + /// Creates a default record suitable for the block at the given local coordinates. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. + /// Thrown when the given block is of a type that does not support a record. + /// Thrown when the block type requests a that has not been registered with the . public void CreateTileEntity (int x, int y, int z) { _tileEntityManager.CreateTileEntity(x, y, z); _dirty = true; } + /// + /// Clears any record set for a block at the givne local coordinates, if one exists. + /// + /// Local X-coordinate of a block. + /// Local Y-coordinate of a block. + /// Local Z-coordinate of a block. public void ClearTileEntity (int x, int y, int z) { _tileEntityManager.ClearTileEntity(x, y, z); @@ -565,7 +907,7 @@ namespace Substrate } - #region IEnumerable Members + /*#region IEnumerable Members public IEnumerator GetEnumerator () { @@ -639,6 +981,6 @@ namespace Substrate } #endregion - } + }*/ } } diff --git a/Substrate/SubstrateCS/Source/BlockTileEntities.cs b/Substrate/SubstrateCS/Source/BlockTileEntities.cs index c368a79..feb57b2 100644 --- a/Substrate/SubstrateCS/Source/BlockTileEntities.cs +++ b/Substrate/SubstrateCS/Source/BlockTileEntities.cs @@ -52,11 +52,11 @@ namespace Substrate { BlockInfoEx info = BlockInfo.BlockTable[_blocks[x, y, z]] as BlockInfoEx; if (info == null) { - return; + throw new InvalidOperationException("The given block is of a type that does not support TileEntities."); } if (te.GetType() != TileEntityFactory.Lookup(info.TileEntityName)) { - return; + throw new ArgumentException("The TileEntity type is not valid for this block.", "te"); } BlockKey key = (TranslateCoordinates != null) @@ -83,12 +83,12 @@ namespace Substrate { BlockInfoEx info = BlockInfo.BlockTable[_blocks[x, y, z]] as BlockInfoEx; if (info == null) { - return; + throw new InvalidOperationException("The given block is of a type that does not support TileEntities."); } TileEntity te = TileEntityFactory.Create(info.TileEntityName); if (te == null) { - return; + throw new UnknownTileEntityException("The TileEntity type '" + info.TileEntityName + "' has not been registered with the TileEntityFactory."); } BlockKey key = (TranslateCoordinates != null) diff --git a/Substrate/SubstrateCS/Source/Chunk.cs b/Substrate/SubstrateCS/Source/Chunk.cs index bad97d8..aba1fdb 100644 --- a/Substrate/SubstrateCS/Source/Chunk.cs +++ b/Substrate/SubstrateCS/Source/Chunk.cs @@ -223,13 +223,7 @@ namespace Substrate _cx = level["xPos"].ToTagInt(); _cz = level["zPos"].ToTagInt(); - _blockManager = new AlphaBlockCollection( - new BlockData(_blocks), - new BlockDataData(_data), - new BlockLightData(_blockLight, _skyLight, _heightMap), - new BlockPropertyData(_tileEntities) - ); - + _blockManager = new AlphaBlockCollection(_blocks, _data, _blockLight, _skyLight, _heightMap, _tileEntities); _entityManager = new EntityCollection(_entities); return this; @@ -322,13 +316,7 @@ namespace Substrate _tree = new NBT_Tree(); _tree.Root.Add("Level", level); - _blockManager = new AlphaBlockCollection( - new BlockData(_blocks), - new BlockDataData(_data), - new BlockLightData(_blockLight, _skyLight, _heightMap), - new BlockPropertyData(_tileEntities) - ); - + _blockManager = new AlphaBlockCollection(_blocks, _data, _blockLight, _skyLight, _heightMap, _tileEntities); _entityManager = new EntityCollection(_entities); } diff --git a/Substrate/SubstrateCS/Source/TileEntityFactory.cs b/Substrate/SubstrateCS/Source/TileEntityFactory.cs index 1ca2bb4..aa03a0e 100644 --- a/Substrate/SubstrateCS/Source/TileEntityFactory.cs +++ b/Substrate/SubstrateCS/Source/TileEntityFactory.cs @@ -63,4 +63,14 @@ namespace Substrate _registry["Trap"] = typeof(TileEntityTrap); } } + + /// + /// An exception that is thrown when unknown TileEntity types are queried. + /// + public class UnknownTileEntityException : Exception + { + public UnknownTileEntityException (string message) + : base(message) + { } + } }