1 /*
2  * Copyright (c) 2017-2018 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: 2017-2018 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 
44 enum Gamemode : ubyte {
45 	
46 	survival = 0, s = 0,
47 	creative = 1, c = 1,
48 	adventure = 2, a = 2,
49 	spectator = 3, sp = 3,
50 	
51 }
52 
53 enum Difficulty : ubyte {
54 	
55 	peaceful = 0,
56 	easy = 1,
57 	normal = 2,
58 	hard = 3,
59 	
60 }
61 
62 enum Dimension : ubyte {
63 	
64 	overworld = 0,
65 	nether = 1,
66 	end = 2,
67 	
68 }
69 
70 /**
71  * Configuration for the server.
72  */
73 class Config {
74 
75 	UUID uuid;
76 
77 	Files files;
78 	LanguageManager lang;
79 
80 	Hub hub;
81 	Node node;
82 
83 	public this(UUID uuid=randomUUID()) {
84 		this.uuid = uuid;
85 	}
86 	
87 	/**
88 	 * Configuration for the hub.
89 	 */
90 	class Hub {
91 
92 		static struct Address {
93 
94 			string ip;
95 			ushort port;
96 
97 			inout string toString() {
98 				return (this.ip.canFind(":") ? "[" ~ this.ip ~ "]" : this.ip) ~ ":" ~ this.port.to!string;
99 			}
100 
101 		}
102 
103 		static struct Game {
104 			
105 			bool enabled;
106 			string motd;
107 			bool onlineMode;
108 			Address[] addresses;
109 			uint[] protocols;
110 			
111 			alias enabled this;
112 			
113 		}
114 
115 		bool edu;
116 
117 		string displayName = "A Minecraft Server";
118 
119 		Game bedrock = Game(true, "A Minecraft Server", false, [Address("0.0.0.0", 19132)], latestBedrockProtocols);
120 		
121 		Game java = Game(true, "A Minecraft Server", false, [Address("0.0.0.0", 25565)], latestJavaProtocols);
122 		
123 		bool allowVanillaPlayers = false;
124 		
125 		bool query = true;
126 		
127 		string serverIp;
128 		
129 		string favicon = "favicon.png";
130 		
131 		bool rcon = false;
132 		
133 		string rconPassword;
134 		
135 		Address[] rconAddresses = [Address("0.0.0.0", 25575)];
136 		
137 		bool webView = false;
138 		
139 		Address[] webViewAddresses = [Address("0.0.0.0", 80), Address("::", 80)];
140 
141 		bool webAdmin = false;
142 
143 		bool webAdminOpen;
144 
145 		Address[] webAdminAddresses = [Address("127.0.0.1", 19134)];
146 
147 		string webAdminPassword = "";
148 
149 		uint webAdminMaxClients = 1;
150 		
151 		JSONValue social;
152 		
153 		string[] acceptedNodes;
154 		
155 		string hncomPassword;
156 		
157 		uint maxNodes = 0;
158 		
159 		ushort hncomPort = 28232;
160 
161 		public this() {
162 			
163 			string randomPassword() {
164 				char[] password = new char[uniform!"[]"(8, 12)];
165 				foreach(ref char c ; password) {
166 					c = uniform!"[]"('a', 'z');
167 					if(!uniform!"[]"(0, 4)) c -= 32;
168 				}
169 				return password.idup;
170 			}
171 
172 			if(lang !is null) {
173 				this.displayName = this.java.motd = this.bedrock.motd = (){
174 					switch(lang.language[0..min(cast(size_t)lang.language.indexOf("_"), $)]) {
175 						case "es": return "Un Servidor de Minecraft";
176 						case "it": return "Un Server di Minecraft";
177 						case "pt": return "Um Servidor de Minecraft";
178 						default: return "A Minecraft Server";
179 					}
180 				}();
181 			}
182 
183 			this.rconPassword = randomPassword();
184 
185 			this.acceptedNodes ~= getAddress("localhost")[0].toAddrString();
186 		}
187 
188 	}
189 
190 	/**
191 	 * Configuration for the node.
192 	 */
193 	class Node {
194 
195 		static struct Game {
196 
197 			bool enabled;
198 			uint[] protocols;
199 
200 			alias enabled this;
201 
202 		}
203 
204 		string name = "node";
205 
206 		string password = "";
207 
208 		string ip;
209 
210 		ushort port = 28232;
211 
212 		bool main = true;
213 
214 		Game java = Game(true, latestJavaProtocols);
215 
216 		Game bedrock = Game(true, latestBedrockProtocols);
217 
218 		uint maxPlayers = 20;
219 
220 		uint gamemode = Gamemode.survival;
221 
222 		uint difficulty = Difficulty.normal;
223 
224 		bool depleteHunger = true;
225 		
226 		bool doDaylightCycle = true;
227 
228 		bool doEntityDrops = true;
229 
230 		bool doFireTick = true;
231 		
232 		bool doScheduledTicks = true;
233 		
234 		bool doWeatherCycle = true;
235 
236 		bool naturalRegeneration = true;
237 		
238 		bool pvp = true;
239 		
240 		uint randomTickSpeed = 3;
241 
242 		uint viewDistance = 10;
243 		
244 		bool aboutCommand = true;
245 
246 		bool clearCommand = true;
247 
248 		bool cloneCommand = true;
249 
250 		bool defaultgamemodeCommand = true;
251 
252 		bool deopCommand = true;
253 
254 		bool difficultyCommand = true;
255 
256 		bool effectCommand = true;
257 
258 		bool enchantCommand = true;
259 
260 		bool experienceCommand = true;
261 
262 		bool executeCommand = true;
263 
264 		bool fillCommand = true;
265 
266 		bool gamemodeCommand = true;
267 
268 		bool gameruleCommand = true;
269 
270 		bool giveCommand = true;
271 
272 		bool helpCommand = true;
273 
274 		bool kickCommand = true;
275 
276 		bool killCommand = true;
277 
278 		bool listCommand = true;
279 
280 		bool locateCommand = true;
281 
282 		bool meCommand = true;
283 
284 		bool opCommand = true;
285 
286 		bool permissionCommand = true;
287 
288 		bool sayCommand = true;
289 
290 		bool seedCommand = true;
291 
292 		bool setmaxplayersCommand = true;
293 
294 		bool setworldspawnCommand = true;
295 
296 		bool spawnpointCommand = true;
297 
298 		bool stopCommand = true;
299 
300 		bool summonCommand = true;
301 
302 		bool titleCommand = true;
303 
304 		bool tellCommand = true;
305 
306 		bool timeCommand = true;
307 
308 		bool toggledownfallCommand = true;
309 
310 		bool tpCommand = true;
311 
312 		bool transferCommand = true;
313 
314 		bool transferserverCommand = true;
315 
316 		bool weatherCommand = true;
317 
318 		bool worldCommand = true;
319 
320 		public this() {
321 
322 			this.ip = getAddress("localhost")[0].toAddrString();
323 
324 		}
325 
326 	}
327 
328 	/**
329 	 * Loads the configuration for the first time.
330 	 */
331 	public void load() {}
332 
333 	/**
334 	 * Reloads the configuration.
335 	 */
336 	public void reload() {}
337 
338 	/**
339 	 * Saves the configuration.
340 	 */
341 	public void save() {}
342 
343 }
344 
345 /**
346  * File manager for assets and temp files.
347  */
348 class Files {
349 	
350 	public immutable string assets;
351 	public immutable string temp;
352 	
353 	public this(string assets, string temp) {
354 		if(!assets.endsWith(dirSeparator)) assets ~= dirSeparator;
355 		this.assets = assets;
356 		if(!temp.endsWith(dirSeparator)) temp ~= dirSeparator;
357 		this.temp = temp;
358 	}
359 	
360 	/**
361 	 * Indicates whether an asset exists.
362 	 * Returns: true if the asset exists, false otherwise
363 	 */
364 	public inout bool hasAsset(string file) {
365 		return exists(this.assets ~ file) && isFile(this.assets ~ file);
366 	}
367 	
368 	/**
369 	 * Reads the content of an asset.
370 	 * Throws: FileException if the file cannot be found.
371 	 */
372 	public inout void[] readAsset(string file) {
373 		return read(this.assets ~ file);
374 	}
375 	
376 	/**
377 	 * Indicates whether a temp file exists.
378 	 */
379 	public inout bool hasTemp(string file) {
380 		return exists(this.temp ~ temp) && isFile(this.temp ~ file);
381 	}
382 	
383 	/**
384 	 * Reads the content of a temp file.
385 	 */
386 	public inout void[] readTemp(string file) {
387 		return read(this.temp ~ file);
388 	}
389 	
390 	/**
391 	 * Writes buffer to a temp file.
392 	 */
393 	public inout void writeTemp(string file, const(void)[] buffer) {
394 		return write(this.temp ~ file, buffer);
395 	}
396 	
397 }
398 
399 //TODO move to selery/lang.d
400 public string bestLanguage(string lang, string[] accepted) {
401 	if(accepted.canFind(lang)) return lang;
402 	string similar = lang[0..lang.indexOf("_")+1];
403 	foreach(al ; accepted) {
404 		if(al.startsWith(similar)) return al;
405 	}
406 	return accepted.canFind("en_GB") ? "en_GB" : accepted[0];
407 }