1 /* 2 * Copyright (c) 2017-2019 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-2019 sel-project 25 * License: MIT 26 * Authors: Kripth 27 * Source: $(HTTP github.com/sel-project/selery/source/selery/config.d, selery/config.d) 28 */ 29 module selery.config; 30 31 import std.algorithm : canFind, min; 32 import std.conv : to; 33 import std.file : exists, isFile, read, write; 34 import std.json : JSONValue; 35 import std.path : dirSeparator; 36 import std.random : uniform; 37 import std.socket : getAddress; 38 import std..string : indexOf, startsWith, endsWith; 39 import std.uuid : UUID, randomUUID; 40 41 import selery.about; 42 import selery.lang : LanguageManager; 43 import selery.plugin : Plugin; 44 45 enum Gamemode : ubyte { 46 47 survival = 0, s = 0, 48 creative = 1, c = 1, 49 adventure = 2, a = 2, 50 spectator = 3, sp = 3, 51 52 } 53 54 enum Difficulty : ubyte { 55 56 peaceful = 0, 57 easy = 1, 58 normal = 2, 59 hard = 3, 60 61 } 62 63 enum Dimension : ubyte { 64 65 overworld = 0, 66 nether = 1, 67 end = 2, 68 69 } 70 71 /** 72 * Configuration for the server. 73 */ 74 class Config { 75 76 UUID uuid; 77 78 Files files; 79 LanguageManager lang; 80 81 Hub hub; 82 Node node; 83 84 public this(UUID uuid=randomUUID()) { 85 this.uuid = uuid; 86 } 87 88 /** 89 * Configuration for the hub. 90 */ 91 class Hub { 92 93 static struct Address { 94 95 string ip; 96 ushort port; 97 98 inout string toString() { 99 return (this.ip.canFind(":") ? "[" ~ this.ip ~ "]" : this.ip) ~ ":" ~ this.port.to!string; 100 } 101 102 } 103 104 static struct Game { 105 106 bool enabled; 107 string motd; 108 bool onlineMode; 109 Address[] addresses; 110 uint[] protocols; 111 112 alias enabled this; 113 114 } 115 116 bool edu; 117 118 string displayName = "A Minecraft Server"; 119 120 Game bedrock = Game(true, "A Minecraft Server", false, [Address("0.0.0.0", 19132)], latestBedrockProtocols); 121 122 Game java = Game(true, "A Minecraft Server", false, [Address("0.0.0.0", 25565)], latestJavaProtocols); 123 124 bool allowVanillaPlayers = false; 125 126 bool query = true; 127 128 string serverIp; 129 130 string favicon = "favicon.png"; 131 132 JSONValue social; 133 134 string[] acceptedNodes; 135 136 string hncomPassword; 137 138 uint maxNodes = 0; 139 140 ushort hncomPort = 28232; 141 142 public this() { 143 144 if(lang !is null) { 145 this.displayName = this.java.motd = this.bedrock.motd = (){ 146 switch(lang.language[0..min(cast(size_t)lang.language.indexOf("_"), $)]) { 147 case "es": return "Un Servidor de Minecraft"; 148 case "it": return "Un Server di Minecraft"; 149 case "pt": return "Um Servidor de Minecraft"; 150 default: return "A Minecraft Server"; 151 } 152 }(); 153 } 154 155 this.acceptedNodes ~= getAddress("localhost")[0].toAddrString(); 156 } 157 158 } 159 160 /** 161 * Configuration for the node. 162 */ 163 class Node { 164 165 static struct Game { 166 167 bool enabled; 168 uint[] protocols; 169 170 alias enabled this; 171 172 } 173 174 string name = "node"; 175 176 string password = ""; 177 178 string ip; 179 180 ushort port = 28232; 181 182 bool main = true; 183 184 Game java = Game(true, latestJavaProtocols); 185 186 Game bedrock = Game(true, latestBedrockProtocols); 187 188 uint maxPlayers = 20; 189 190 Gamemode gamemode = Gamemode.survival; 191 192 Difficulty difficulty = Difficulty.normal; 193 194 bool depleteHunger = true; 195 196 bool doDaylightCycle = true; 197 198 bool doEntityDrops = true; 199 200 bool doFireTick = true; 201 202 bool doScheduledTicks = true; 203 204 bool doWeatherCycle = true; 205 206 bool naturalRegeneration = true; 207 208 bool pvp = true; 209 210 uint randomTickSpeed = 3; 211 212 uint viewDistance = 10; 213 214 bool aboutCommand = true; 215 216 bool helpCommand = true; 217 218 bool permissionCommand = true; 219 220 bool stopCommand = true; 221 222 bool transferCommand = true; 223 224 bool worldCommand = true; 225 226 public this() { 227 228 this.ip = getAddress("localhost")[0].toAddrString(); 229 230 } 231 232 } 233 234 /** 235 * Loads the configuration for the first time. 236 */ 237 public void load() {} 238 239 /** 240 * Reloads the configuration. 241 */ 242 public void reload() {} 243 244 /** 245 * Saves the configuration. 246 */ 247 public void save() {} 248 249 } 250 251 /** 252 * File manager for assets and temp files. 253 */ 254 class Files { 255 256 public immutable string assets; 257 public immutable string temp; 258 259 public this(string assets, string temp) { 260 if(!assets.endsWith(dirSeparator)) assets ~= dirSeparator; 261 this.assets = assets; 262 if(!temp.endsWith(dirSeparator)) temp ~= dirSeparator; 263 this.temp = temp; 264 } 265 266 /** 267 * Indicates whether an asset exists. 268 * Returns: true if the asset exists, false otherwise 269 */ 270 public inout bool hasAsset(string file) { 271 return exists(this.assets ~ file) && isFile(this.assets ~ file); 272 } 273 274 /** 275 * Reads the content of an asset. 276 * Throws: FileException if the file cannot be found. 277 */ 278 public inout void[] readAsset(string file) { 279 return read(this.assets ~ file); 280 } 281 282 public inout bool hasPluginAsset(Plugin plugin, string file) { 283 return exists(plugin.path ~ "assets" ~ dirSeparator ~ file); 284 } 285 286 public inout void[] readPluginAsset(Plugin plugin, string file) { 287 return read(plugin.path ~ "assets" ~ dirSeparator ~ file); 288 } 289 290 /** 291 * Indicates whether a temp file exists. 292 */ 293 public inout bool hasTemp(string file) { 294 return exists(this.temp ~ temp) && isFile(this.temp ~ file); 295 } 296 297 /** 298 * Reads the content of a temp file. 299 */ 300 public inout void[] readTemp(string file) { 301 return read(this.temp ~ file); 302 } 303 304 /** 305 * Writes buffer to a temp file. 306 */ 307 public inout void writeTemp(string file, const(void)[] buffer) { 308 return write(this.temp ~ file, buffer); 309 } 310 311 } 312 313 //TODO move to selery/lang.d 314 public string bestLanguage(string lang, string[] accepted) { 315 if(accepted.canFind(lang)) return lang; 316 string similar = lang[0..lang.indexOf("_")+1]; 317 foreach(al ; accepted) { 318 if(al.startsWith(similar)) return al; 319 } 320 return accepted.canFind("en_GB") ? "en_GB" : accepted[0]; 321 }