1 /**
2 Copyright: Copyright (c) 2016-2018 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 
7 module enginestarter;
8 
9 import std.file : mkdirRecurse, getcwd;
10 import std.path : buildPath;
11 import std.getopt;
12 import core.thread : Thread, thread_joinAll;
13 import voxelman.log;
14 import pluginlib;
15 import pluginlib.plugininforeader : filterEnabledPlugins;
16 import pluginlib.pluginmanager;
17 
18 
19 struct EngineStarter
20 {
21 	enum AppType { client, server, combined }
22 
23 	void start(string[] args)
24 	{
25 		AppType appType = AppType.combined;
26 		bool logToConsole;
27 		std.getopt.getopt(args,
28 			std.getopt.config.passThrough,
29 			"app", &appType,
30 			"console_log", &logToConsole);
31 
32 		setupLogs(appType, logToConsole);
33 		scope(exit) closeBinLog();
34 
35 		infof("Started from '%s'", getcwd);
36 
37 		final switch(appType) with(AppType)
38 		{
39 			case client: startClient(args); break;
40 			case server: startServer(args, ServerMode.standalone); break;
41 			case combined: startCombined(args); break;
42 		}
43 
44 		waitForThreads();
45 	}
46 
47 	void setupLogs(AppType appType, bool logToConsole = true, string logsFolder = "../logs")
48 	{
49 		mkdirRecurse(logsFolder);
50 		enum textFormat = ".log";
51 		enum binFormat = ".bin";
52 
53 		string name;
54 		final switch(appType) with(AppType)
55 		{
56 			case client: name = "client"; break;
57 			case server: name = "server"; break;
58 			case combined: name = "client"; break;
59 		}
60 
61 		auto logger = setupMultiLogger();
62 		setupFileLogger(logger, buildPath(logsFolder, name ~ textFormat));
63 		if (logToConsole)
64 			setupStdoutLogger(logger);
65 
66 		initBinLog(buildPath(logsFolder, name ~ binFormat));
67 	}
68 
69 	void startServer(string[] args, ServerMode serverMode)
70 	{
71 		infof("Server thread: %s", Thread.getThis.id);
72 		try {
73 			auto pluginman = new PluginManager;
74 			foreach(p; pluginRegistry.serverPlugins.byValue.filterEnabledPlugins(args))
75 				pluginman.registerPlugin(p);
76 			pluginman.initPlugins();
77 			pluginRegistry.serverMain(args, serverMode);
78 		} catch(Throwable t) {
79 			criticalf("Server failed with error");
80 			criticalf("%s", t);
81 		}
82 	}
83 
84 	void startClient(string[] args)
85 	{
86 		infof("Client thread: %s", Thread.getThis.id);
87 		try {
88 			auto pluginman = new PluginManager;
89 			foreach(p; pluginRegistry.clientPlugins.byValue.filterEnabledPlugins(args))
90 				pluginman.registerPlugin(p);
91 			pluginman.initPlugins();
92 			pluginRegistry.clientMain(args);
93 		} catch(Throwable t) {
94 			criticalf("Client failed with error");
95 			criticalf("%s", t);
96 		}
97 	}
98 
99 	void startCombined(string[] args)
100 	{
101 		import voxelman.thread.servercontrol : stopServer;
102 
103 		void exec()
104 		{
105 			startServer(args, ServerMode.internal);
106 		}
107 
108 		Thread serverThread = new Thread(&exec);
109 		serverThread.start();
110 
111 		startClient(args);
112 		stopServer();
113 	}
114 
115 	void waitForThreads()
116 	{
117 		thread_joinAll();
118 		infof("[Stopped]");
119 	}
120 }