using System; using System.IO; using Substrate.Core; using Substrate.Nbt; namespace Substrate { /// /// Encompases data to specify game rules. /// public class GameRules : ICopyable { private string _commandBlockOutput = "true"; private string _doFireTick = "true"; private string _doMobLoot = "true"; private string _doMobSpawning = "true"; private string _doTileDrops = "true"; private string _keepInventory = "false"; private string _mobGriefing = "true"; /// /// Gets or sets whether or not actions performed by command blocks are displayed in the chat. /// public bool CommandBlockOutput { get { return _commandBlockOutput == "true"; } set { _commandBlockOutput = value ? "true" : "false"; } } /// /// Gets or sets whether to spread or remove fire. /// public bool DoFireTick { get { return _doFireTick == "true"; } set { _doFireTick = value ? "true" : "false"; ; } } /// /// Gets or sets whether mobs should drop loot when killed. /// public bool DoMobLoot { get { return _doMobLoot == "true"; } set { _doMobLoot = value ? "true" : "false"; ; } } /// /// Gets or sets whether mobs should spawn naturally. /// public bool DoMobSpawning { get { return _doMobSpawning == "true"; } set { _doMobSpawning = value ? "true" : "false"; ; } } /// /// Gets or sets whether breaking blocks should drop the block's item drop. /// public bool DoTileDrops { get { return _doTileDrops == "true"; } set { _doTileDrops = value ? "true" : "false"; ; } } /// /// Gets or sets whether players keep their inventory after they die. /// public bool KeepInventory { get { return _keepInventory == "true"; } set { _keepInventory = value ? "true" : "false"; ; } } /// /// Gets or sets whether mobs can destroy blocks (creeper explosions, zombies breaking doors, etc.). /// public bool MobGriefing { get { return _mobGriefing == "true"; } set { _mobGriefing = value ? "true" : "false"; ; } } #region ICopyable Members /// public GameRules Copy () { GameRules gr = new GameRules(); gr._commandBlockOutput = _commandBlockOutput; gr._doFireTick = _doFireTick; gr._doMobLoot = _doMobLoot; gr._doMobSpawning = _doMobSpawning; gr._doTileDrops = _doTileDrops; gr._keepInventory = _keepInventory; gr._mobGriefing = _mobGriefing; return gr; } #endregion } /// /// Specifies the type of gameplay associated with a world. /// public enum GameType { /// /// The world will be played in Survival mode. /// SURVIVAL = 0, /// /// The world will be played in Creative mode. /// CREATIVE = 1, } public enum TimeOfDay { Daytime = 0, Noon = 6000, Sunset = 12000, Nighttime = 13800, Midnight = 18000, Sunrise = 22200, } /// /// Represents general data and metadata of a single world. /// public class Level : INbtObject, ICopyable { private static SchemaNodeCompound _schema = new SchemaNodeCompound() { new SchemaNodeCompound("Data") { new SchemaNodeScaler("Time", TagType.TAG_LONG), new SchemaNodeScaler("LastPlayed", TagType.TAG_LONG, SchemaOptions.CREATE_ON_MISSING), new SchemaNodeCompound("Player", Player.Schema, SchemaOptions.OPTIONAL), new SchemaNodeScaler("SpawnX", TagType.TAG_INT), new SchemaNodeScaler("SpawnY", TagType.TAG_INT), new SchemaNodeScaler("SpawnZ", TagType.TAG_INT), new SchemaNodeScaler("SizeOnDisk", TagType.TAG_LONG, SchemaOptions.CREATE_ON_MISSING), new SchemaNodeScaler("RandomSeed", TagType.TAG_LONG), new SchemaNodeScaler("version", TagType.TAG_INT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("LevelName", TagType.TAG_STRING, SchemaOptions.OPTIONAL), new SchemaNodeScaler("generatorName", TagType.TAG_STRING, SchemaOptions.OPTIONAL), new SchemaNodeScaler("raining", TagType.TAG_BYTE, SchemaOptions.OPTIONAL), new SchemaNodeScaler("thundering", TagType.TAG_BYTE, SchemaOptions.OPTIONAL), new SchemaNodeScaler("rainTime", TagType.TAG_INT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("thunderTime", TagType.TAG_INT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("GameType", TagType.TAG_INT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("MapFeatures", TagType.TAG_BYTE, SchemaOptions.OPTIONAL), new SchemaNodeScaler("hardcore", TagType.TAG_BYTE, SchemaOptions.OPTIONAL), new SchemaNodeScaler("generatorVersion", TagType.TAG_INT, SchemaOptions.OPTIONAL), new SchemaNodeScaler("generatorOptions", TagType.TAG_STRING, SchemaOptions.OPTIONAL), new SchemaNodeScaler("initialized", TagType.TAG_BYTE, SchemaOptions.OPTIONAL), new SchemaNodeScaler("allowCommands", TagType.TAG_BYTE, SchemaOptions.OPTIONAL), new SchemaNodeScaler("DayTime", TagType.TAG_LONG, SchemaOptions.OPTIONAL), new SchemaNodeCompound("GameRules") { new SchemaNodeScaler("commandBlockOutput", TagType.TAG_STRING), new SchemaNodeScaler("doFireTick", TagType.TAG_STRING), new SchemaNodeScaler("doMobLoot", TagType.TAG_STRING), new SchemaNodeScaler("doMobSpawning", TagType.TAG_STRING), new SchemaNodeScaler("doTileDrops", TagType.TAG_STRING), new SchemaNodeScaler("keepInventory", TagType.TAG_STRING), new SchemaNodeScaler("mobGriefing", TagType.TAG_STRING), }, }, }; private TagNodeCompound _source; private NbtWorld _world; private long _time; private long _lastPlayed; private Player _player; private int _spawnX; private int _spawnY; private int _spawnZ; private long _sizeOnDisk; private long _randomSeed; private int? _version; private string _name; private string _generator; private byte? _raining; private byte? _thundering; private int? _rainTime; private int? _thunderTime; private int? _gameType; private byte? _mapFeatures; private byte? _hardcore; private int? _generatorVersion; private string _generatorOptions; private byte? _initialized; private byte? _allowCommands; private long? _DayTime; private GameRules _gameRules; /// /// Gets or sets the creation time of the world as a long timestamp. /// public long Time { get { return _time; } set { _time = value; } } /// /// Gets or sets the time that the world was last played as a long timestamp. /// public long LastPlayed { get { return _lastPlayed; } set { _lastPlayed = value; } } /// /// Gets or sets the player for single-player worlds. /// public Player Player { get { return _player; } set { _player = value; _player.World = _name; } } /// /// Gets or sets the world's spawn point. /// public SpawnPoint Spawn { get { return new SpawnPoint(_spawnX, _spawnY, _spawnZ); } set { _spawnX = value.X; _spawnY = value.Y; _spawnZ = value.Z; } } /// /// Gets the estimated size of the world in bytes. /// public long SizeOnDisk { get { return _sizeOnDisk; } } /// /// Gets or sets the world's random seed. /// public long RandomSeed { get { return _randomSeed; } set { _randomSeed = value; } } /// /// Gets or sets the world's version number. /// public int Version { get { return _version ?? 0; } set { _version = value; } } /// /// Gets or sets the name of the world. /// /// If there is a object attached to this world, the player's world field /// will also be updated. public string LevelName { get { return _name; } set { _name = value; if (_player != null) { _player.World = value; } } } /// /// Gets or sets the name of the world generator. /// /// This should be 'default', 'flat', or 'largeBiomes'. public string GeneratorName { get { return _generator; } set { _generator = value; } } /// /// Gets or sets a value indicating that it is raining in the world. /// public bool IsRaining { get { return (_raining ?? 0) == 1; } set { _raining = value ? (byte)1 : (byte)0; } } /// /// Gets or sets a value indicating that it is thunderstorming in the world. /// public bool IsThundering { get { return (_thundering ?? 0) == 1; } set { _thundering = value ? (byte)1 : (byte)0; } } /// /// Gets or sets the timer value for controlling rain. /// public int RainTime { get { return _rainTime ?? 0; } set { _rainTime = value; } } /// /// Gets or sets the timer value for controlling thunderstorms. /// public int ThunderTime { get { return _thunderTime ?? 0; } set { _thunderTime = value; } } /// /// Gets or sets the game type associated with this world. /// public GameType GameType { get { return (GameType)(_gameType ?? 0); } set { _gameType = (int)value; } } /// /// Gets or sets a value indicating that structures (dungeons, villages, ...) will be generated. /// public bool UseMapFeatures { get { return (_mapFeatures ?? 0) == 1; } set { _mapFeatures = value ? (byte)1 : (byte)0; } } /// /// Gets or sets a value indicating whether the map is hardcore mode /// public bool Hardcore { get { return (_hardcore ?? 0) == 1; } set { _hardcore = value ? (byte)1 : (byte)0; } } /// /// Gets or sets a value indicating the version of the level generator /// public int GeneratorVersion { get { return _generatorVersion ?? 0; } set { _generatorVersion = value; } } /// /// Gets or sets a value indicating controls options for the generator, /// currently only the Superflat generator. The format is a comma separated /// list of block IDs from the bottom of the map up, and each block ID may /// optionally be preceded by the number of layers and an x. /// Damage values are not supported. /// public string GeneratorOptions { get { return _generatorOptions ?? ""; } set { _generatorOptions = value; } } /// /// Gets or sets a value, normally true, indicating whether a world has been /// initialized properly after creation. If the initial simulation was canceled /// somehow, this can be false and the world will be re-initialized on next load. /// public bool Initialized { get { return (_generatorVersion ?? 0) == 1; } set { _generatorVersion = value ? (byte)1 : (byte)0; } } /// /// Gets or sets a value indicating if cheats are enabled. /// public bool AllowCommands { get { return (_allowCommands ?? 0) == 1; } set { _allowCommands = value ? (byte)1 : (byte)0; } } /// /// Gets or sets a value indicating the time of day. /// 0 is sunrise, 6000 is midday, 12000 is sunset, /// 18000 is midnight, 24000 is the next day's 0. /// This value keeps counting past 24000 and does not reset to 0 /// public long DayTime { get { return _DayTime ?? 0; } set { _DayTime = value; } } /// /// Gets the level's game rules. /// public GameRules GameRules { get { return _gameRules; } } /// /// Gets the source used to create this if it exists. /// public TagNodeCompound Source { get { return _source; } } /// /// Gets a representing the schema of a level. /// public static SchemaNodeCompound Schema { get { return _schema; } } /// /// Creates a new object with reasonable defaults tied to the given world. /// /// The world that the should be tied to. public Level (NbtWorld world) { _world = world; // Sane defaults _time = 0; _lastPlayed = 0; _spawnX = 0; _spawnY = 64; _spawnZ = 0; _sizeOnDisk = 0; _randomSeed = new Random().Next(); //_version = 19132; _version = 19133; _name = "Untitled"; _generator = "default"; _hardcore = 0; _generatorOptions = ""; _generatorVersion = 1; _initialized = 0; _allowCommands = 0; _DayTime = 0; _gameRules = new GameRules(); GameType = GameType.SURVIVAL; UseMapFeatures = true; _source = new TagNodeCompound(); } /// /// Creates a copy of an existing object. /// /// The object to copy. protected Level (Level p) { _world = p._world; _time = p._time; _lastPlayed = p._lastPlayed; _spawnX = p._spawnX; _spawnY = p._spawnY; _spawnZ = p._spawnZ; _sizeOnDisk = p._sizeOnDisk; _randomSeed = p._randomSeed; _version = p._version; _name = p._name; _generator = p._generator; _raining = p._raining; _thundering = p._thundering; _rainTime = p._rainTime; _thunderTime = p._thunderTime; _gameType = p._gameType; _mapFeatures = p._mapFeatures; _hardcore = p._hardcore; _generatorVersion = p._generatorVersion; _generatorOptions = p._generatorOptions; _initialized = p._initialized; _allowCommands = p._allowCommands; _DayTime = p._DayTime; _gameRules = p._gameRules.Copy(); if (p._player != null) { _player = p._player.Copy(); } if (p._source != null) { _source = p._source.Copy() as TagNodeCompound; } } /// /// Creates a default player entry for this world. /// public void SetDefaultPlayer () { _player = new Player(); _player.World = _name; _player.Position.X = _spawnX; _player.Position.Y = _spawnY + 1.7; _player.Position.Z = _spawnZ; } /// /// Saves a object to disk as a standard compressed NBT stream. /// /// True if the level was saved; false otherwise. /// Thrown when an error is encountered writing out the level. public bool Save () { if (_world == null) { return false; } try { NBTFile nf = new NBTFile(Path.Combine(_world.Path, "level.dat")); Stream zipstr = nf.GetDataOutputStream(); if (zipstr == null) { NbtIOException nex = new NbtIOException("Failed to initialize compressed NBT stream for output"); nex.Data["Level"] = this; throw nex; } new NbtTree(BuildTree() as TagNodeCompound).WriteTo(zipstr); zipstr.Close(); return true; } catch (Exception ex) { LevelIOException lex = new LevelIOException("Could not save level file.", ex); lex.Data["Level"] = this; throw lex; } } #region INBTObject Members /// /// Attempt to load a Level subtree into the without validation. /// /// The root node of a Level subtree. /// The returns itself on success, or null if the tree was unparsable. public virtual Level LoadTree (TagNode tree) { TagNodeCompound dtree = tree as TagNodeCompound; if (dtree == null) { return null; } _version = null; _raining = null; _rainTime = null; _thundering = null; _thunderTime = null; _gameType = null; _mapFeatures = null; _generatorOptions = null; _generatorVersion = null; _allowCommands = null; _initialized = null; _DayTime = null; TagNodeCompound ctree = dtree["Data"].ToTagCompound(); _time = ctree["Time"].ToTagLong(); _lastPlayed = ctree["LastPlayed"].ToTagLong(); if (ctree.ContainsKey("Player")) { _player = new Player().LoadTree(ctree["Player"]); } _spawnX = ctree["SpawnX"].ToTagInt(); _spawnY = ctree["SpawnY"].ToTagInt(); _spawnZ = ctree["SpawnZ"].ToTagInt(); _sizeOnDisk = ctree["SizeOnDisk"].ToTagLong(); _randomSeed = ctree["RandomSeed"].ToTagLong(); if (ctree.ContainsKey("version")) { _version = ctree["version"].ToTagInt(); } if (ctree.ContainsKey("LevelName")) { _name = ctree["LevelName"].ToTagString(); } if (ctree.ContainsKey("generatorName")) { _generator = ctree["generatorName"].ToTagString(); } if (ctree.ContainsKey("raining")) { _raining = ctree["raining"].ToTagByte(); } if (ctree.ContainsKey("thundering")) { _thundering = ctree["thundering"].ToTagByte(); } if (ctree.ContainsKey("rainTime")) { _rainTime = ctree["rainTime"].ToTagInt(); } if (ctree.ContainsKey("thunderTime")) { _thunderTime = ctree["thunderTime"].ToTagInt(); } if (ctree.ContainsKey("GameType")) { _gameType = ctree["GameType"].ToTagInt(); } if (ctree.ContainsKey("MapFeatures")) { _mapFeatures = ctree["MapFeatures"].ToTagByte(); } if (ctree.ContainsKey("hardcore")) { _hardcore = ctree["hardcore"].ToTagByte(); } if (ctree.ContainsKey("generatorVersion")) { _generatorVersion = ctree["generatorVersion"].ToTagInt(); } if (ctree.ContainsKey("generatorOptions")) { _generatorOptions = ctree["generatorOptions"].ToTagString(); } if (ctree.ContainsKey("allowCommands")) { _allowCommands = ctree["allowCommands"].ToTagByte(); } if (ctree.ContainsKey("initialized")) { _initialized = ctree["initialized"].ToTagByte(); } if (ctree.ContainsKey("DayTime")) { _DayTime = ctree["DayTime"].ToTagLong(); } if (ctree.ContainsKey("GameRules")) { TagNodeCompound gr = ctree["GameRules"].ToTagCompound(); _gameRules = new GameRules(); _gameRules.CommandBlockOutput = gr["commandBlockOutput"].ToTagString().Data == "true"; _gameRules.DoFireTick = gr["doFireTick"].ToTagString().Data == "true"; _gameRules.DoMobLoot = gr["doMobLoot"].ToTagString().Data == "true"; _gameRules.DoMobSpawning = gr["doMobSpawning"].ToTagString().Data == "true"; _gameRules.DoTileDrops = gr["doTileDrops"].ToTagString().Data == "true"; _gameRules.KeepInventory = gr["keepInventory"].ToTagString().Data == "true"; _gameRules.MobGriefing = gr["mobGriefing"].ToTagString().Data == "true"; } _source = ctree.Copy() as TagNodeCompound; return this; } /// /// Attempt to load a Level subtree into the with validation. /// /// The root node of a Level subtree. /// The returns itself on success, or null if the tree failed validation. public virtual Level LoadTreeSafe (TagNode tree) { if (!ValidateTree(tree)) { return null; } return LoadTree(tree); } /// /// Builds a Level subtree from the current data. /// /// The root node of a Level subtree representing the current data. public virtual TagNode BuildTree () { TagNodeCompound data = new TagNodeCompound(); data["Time"] = new TagNodeLong(_time); data["LastPlayed"] = new TagNodeLong(_lastPlayed); if (_player != null) { data["Player"] = _player.BuildTree(); } data["SpawnX"] = new TagNodeInt(_spawnX); data["SpawnY"] = new TagNodeInt(_spawnY); data["SpawnZ"] = new TagNodeInt(_spawnZ); data["SizeOnDisk"] = new TagNodeLong(_sizeOnDisk); data["RandomSeed"] = new TagNodeLong(_randomSeed); if (_version != null && _version != 0) { data["version"] = new TagNodeInt(_version ?? 0); } if (_name != null) { data["LevelName"] = new TagNodeString(_name); } if (_generator != null) { data["generatorName"] = new TagNodeString(_generator); } if (_raining != null) { data["raining"] = new TagNodeByte(_raining ?? 0); } if (_thundering != null) { data["thundering"] = new TagNodeByte(_thundering ?? 0); } if (_rainTime != null) { data["rainTime"] = new TagNodeInt(_rainTime ?? 0); } if (_thunderTime != null) { data["thunderTime"] = new TagNodeInt(_thunderTime ?? 0); } if (_gameType != null) { data["GameType"] = new TagNodeInt(_gameType ?? 0); } if (_mapFeatures != null) { data["MapFeatures"] = new TagNodeByte(_mapFeatures ?? 0); } if (_hardcore != null) { data["hardcore"] = new TagNodeByte(_hardcore ?? 0); } if (_generatorOptions != null) { data["generatorOptions"] = new TagNodeString(_generatorOptions); } if (_generatorVersion != null) { data["generatorVersion"] = new TagNodeInt(_generatorVersion ?? 0); } if (_allowCommands != null) { data["allowCommands"] = new TagNodeByte(_allowCommands ?? 0); } if (_initialized != null) { data["initialized"] = new TagNodeByte(_initialized ?? 0); } if (_DayTime != null) { data["DayTime"] = new TagNodeLong(_DayTime ?? 0); } TagNodeCompound gr = new TagNodeCompound(); gr["commandBlockOutput"] = new TagNodeString(_gameRules.CommandBlockOutput ? "true" : "false"); gr["doFireTick"] = new TagNodeString(_gameRules.DoFireTick ? "true" : "false"); gr["doMobLoot"] = new TagNodeString(_gameRules.DoMobLoot ? "true" : "false"); gr["doMobSpawning"] = new TagNodeString(_gameRules.DoMobSpawning ? "true" : "false"); gr["doTileDrops"] = new TagNodeString(_gameRules.DoTileDrops ? "true" : "false"); gr["keepInventory"] = new TagNodeString(_gameRules.KeepInventory ? "true" : "false"); gr["mobGriefing"] = new TagNodeString(_gameRules.MobGriefing ? "true" : "false"); data["GameRules"] = gr; if (_source != null) { data.MergeFrom(_source); } TagNodeCompound tree = new TagNodeCompound(); tree.Add("Data", data); return tree; } /// /// Validate a Level subtree against a schema defintion. /// /// The root node of a Level subtree. /// Status indicating whether the tree was valid against the internal schema. public virtual bool ValidateTree (TagNode tree) { return new NbtVerifier(tree, _schema).Verify(); } #endregion #region ICopyable Members /// /// Creates a deep-copy of the . /// /// A deep-copy of the , including a copy of the , if one is attached. public virtual Level Copy () { return new Level(this); } #endregion } }