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 }