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/hub/handler/handler.d, selery/hub/handler/handler.d)
28  */
29 module selery.hub.handler.handler;
30 
31 import std.json : JSONValue;
32 import std.socket : SocketException;
33 import std.string : toLower, indexOf, strip, split, join;
34 
35 import sel.server.bedrock : BedrockServerImpl;
36 import sel.server.java : JavaServerImpl;
37 import sel.server.query : Query;
38 import sel.server.util : ServerInfo, GenericServer;
39 
40 import selery.about;
41 import selery.config : Config;
42 import selery.hub.handler.hncom : HncomHandler, LiteNode;
43 import selery.hub.handler.rcon : RconHandler;
44 import selery.hub.handler.webadmin : WebAdminHandler;
45 import selery.hub.handler.webview : WebViewHandler;
46 import selery.hub.server : HubServer;
47 import selery.lang : Translation;
48 import selery.log : Format;
49 import selery.util.thread : SafeThread;
50 
51 /**
52  * Main handler with the purpose of starting children handlers,
53  * store constant informations and reload them when needed.
54  */
55 class Handler {
56 
57 	private shared HubServer server;
58 
59 	private shared JSONValue additionalJson;
60 	private shared string socialJson; // already encoded
61 
62 	public shared this(shared HubServer server, shared ServerInfo info, shared Query _query) {
63 
64 		this.server = server;
65 		
66 		this.regenerateSocialJson();
67 
68 		bool delegate(string ip) acceptIp; //TODO must be implemented by sel-server
69 		immutable forcedIp = server.config.hub.serverIp.toLower;
70 		if(forcedIp.length) {
71 			acceptIp = (string ip){ return ip.toLower == forcedIp; };
72 		} else {
73 			acceptIp = (string ip){ return true; };
74 		}
75 
76 		// start handlers
77 
78 		void startGenericServer(shared GenericServer gs, string name, const(Config.Hub.Address)[] addresses) {
79 			foreach(address ; addresses) {
80 				try {
81 					gs.start(address.ip, address.port, _query);
82 					debug server.logger.log(Translation("handler.listening", [Format.green ~ name ~ Format.reset, address.toString()]));
83 				} catch(SocketException e) {
84 					server.logger.logError(Translation("handler.error.bind", [name, address.toString(), (e.msg.indexOf(":")!=-1 ? e.msg.split(":")[$-1].strip : e.msg)]));
85 				} catch(Throwable t) {
86 					server.logger.logError(Translation("handler.error.address", [name, address.toString()]));
87 				}
88 			}
89 		}
90 
91 		with(server.config.hub) {
92 
93 			if(!server.lite) {
94 				auto s = new shared HncomHandler(server, &this.additionalJson);
95 				s.start(acceptedNodes, hncomPort);
96 			} else {
97 				new SafeThread(server.config.lang, { new shared LiteNode(server, &this.additionalJson); }).start();
98 			}
99 
100 			if(bedrock) {
101 				auto s = new shared BedrockServerImpl!supportedBedrockProtocols(info, server);
102 				startGenericServer(s, "bedrock", bedrock.addresses);
103 			}
104 
105 			if(java) {
106 				auto s = new shared JavaServerImpl!supportedJavaProtocols(info, server);
107 				startGenericServer(s, "java", java.addresses);
108 			}
109 
110 			if(rcon) {
111 				auto s = new shared RconHandler(server);
112 				startGenericServer(s, "rcon", rconAddresses);
113 			}
114 
115 			if(webView) {
116 				auto s = new shared WebViewHandler(server, &this.socialJson);
117 				startGenericServer(s, "web_view", webViewAddresses);
118 			}
119 
120 			if(webAdmin) {
121 				auto s = new shared WebAdminHandler(server);
122 				startGenericServer(s, "web_admin", webAdminAddresses);
123 			}
124 
125 		}
126 
127 	}
128 
129 	/**
130 	 * Regenerates the social json adding a string field
131 	 * for each social field that is not empty in the settings.
132 	 */
133 	private shared void regenerateSocialJson() {
134 		const config = this.server.config;
135 		this.socialJson = config.hub.social.toString();
136 		JSONValue[string] additional;
137 		additional["social"] = config.hub.social;
138 		additional["minecraft"] = ["edu": config.hub.edu];
139 		additional["software"] = ["name": Software.name, "version": Software.displayVersion];
140 		this.additionalJson = cast(shared)JSONValue(additional);
141 	}
142 
143 	/**
144 	 * Closes the handlers and frees the resources.
145 	 */
146 	public shared void shutdown() {
147 		//TODO gracefully shutdown every thread
148 	}
149 
150 }
151 
152 deprecated("Server is never reloaded") interface Reloadable {
153 
154 	public shared void reload();
155 
156 }