1 /* 2 * Copyright (c) 2017-2020 sel-project 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a copy 5 * of this software and associated documentation files (the "Software"), to deal 6 * in the Software without restriction, including without limitation the rights 7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 * copies of the Software, and to permit persons to whom the Software is 9 * furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice shall be included in all 12 * copies or substantial portions of the Software. 13 * 14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 * SOFTWARE. 21 * 22 */ 23 /** 24 * Copyright: Copyright (c) 2017-2020 sel-project 25 * License: MIT 26 * Authors: Kripth 27 * Source: $(HTTP github.com/sel-project/sel-level/sel/level/level.d, sel/level/level.d) 28 */ 29 module sel.level.level; 30 31 import std.conv : to, ConvException; 32 import std.path : buildNormalizedPath, dirSeparator; 33 34 import sel.level.data; 35 import sel.level.exception : LevelInfoException, ChunkException; 36 import sel.level.util; 37 38 import sel.math : Vector2; 39 40 import sel.nbt.tags : Named, Compound; 41 42 abstract class Level { 43 44 public immutable string path; 45 46 private bool levelInfoLoaded = false; 47 private LevelInfo _levelInfo; 48 49 public Chunk[Vector2!int] chunks; 50 51 public this(string path) { 52 this.path = buildNormalizedPath(path) ~ dirSeparator; 53 } 54 55 /** 56 * Throws: LevelInfoExeption if level.dat has an invalid format. 57 */ 58 public final @property ref LevelInfo levelInfo() { 59 if(!this.levelInfoLoaded) this.reloadLevelInfo(); 60 return this._levelInfo; 61 } 62 63 public final @property ref LevelInfo levelInfo(LevelInfo levelInfo) { 64 this.levelInfoLoaded = true; 65 return this._levelInfo = _levelInfo; 66 } 67 68 public final void reloadLevelInfo() { 69 this.levelInfoLoaded = true; 70 this._levelInfo = this.readLevelInfo(); 71 } 72 73 protected abstract LevelInfo readLevelInfo(); 74 75 protected abstract void writeLevelInfo(LevelInfo); 76 77 /** 78 * Reads the chunk at the given coordinates. 79 * Throws: ChunkException 80 */ 81 public final Chunk readChunk(Dimension dimension, Vector2!int position) { 82 return this.readChunkImpl(dimension, position); 83 } 84 85 /// ditto 86 public final Chunk readChunk(Dimension dimension, int x, int z) { 87 return this.readChunk(dimension, Vector2!int(x, z)); 88 } 89 90 /// ditto 91 public final Chunk readChunk(Vector2!int position) { 92 return this.readChunk(Dimension.overworld, position); 93 } 94 95 /// ditto 96 public final Chunk readChunk(int x, int z) { 97 return this.readChunk(Vector2!int(x, z)); 98 } 99 100 protected abstract Chunk readChunkImpl(Dimension, Vector2!int); 101 102 /** 103 * Reads all chunks in the level. 104 * Throws: ChunkException 105 */ 106 public final ReadChunksResult readChunks(Dimension dimension=Dimension.overworld) { 107 return this.readChunksImpl(dimension); 108 } 109 110 protected abstract ReadChunksResult readChunksImpl(Dimension); 111 112 } 113 114 struct ReadChunksResult { 115 116 Chunk[Vector2!int] chunks; 117 ChunkException[] exceptions; 118 119 } 120 121 LevelInfo readLevelInfoCompound(Info...)(Compound compound) if(Info.length % 3 == 0) { 122 LevelInfo ret; 123 foreach(i, T; Info) { 124 static if(i % 3 == 0) { 125 auto tag = Info[i+2] in compound; 126 if(tag && cast(T)*tag) { 127 try { 128 mixin("ret." ~ Info[i+1]) = to!(typeof(mixin("ret." ~ Info[i+1])))((cast(T)*tag).value); 129 } catch(ConvException) { 130 throw new LevelInfoException(LevelInfoException.WRONG_VALUE, "Tag " ~ Info[i+2] ~ " cannot be converted to " ~ typeof(mixin("ret." ~ Info[i+1])).stringof); 131 } 132 } 133 } 134 } 135 return ret; 136 } 137 138 Compound writeLevelInfoCompound(Info...)(LevelInfo levelInfo) if(Info.length % 3 == 0) { 139 Compound ret = new Compound(); 140 foreach(i, T; Info) { 141 static if(i % 3 == 0) ret[] = new Named!T(Info[i+2], mixin("levelInfo." ~ Info[i+1])); 142 } 143 return ret; 144 }