Grasscutter.java 8.18 KB
Newer Older
Melledy's avatar
Melledy committed
1
2
3
4
5
package emu.grasscutter;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
6
import java.io.IOError;
Melledy's avatar
Melledy committed
7
import java.net.InetSocketAddress;
8
import java.util.Calendar;
Secretboy's avatar
Secretboy committed
9
import java.util.Locale;
Melledy's avatar
Melledy committed
10

Jaida Wu's avatar
Jaida Wu committed
11
import emu.grasscutter.command.CommandMap;
KingRainbow44's avatar
KingRainbow44 committed
12
import emu.grasscutter.plugin.PluginManager;
KingRainbow44's avatar
KingRainbow44 committed
13
import emu.grasscutter.plugin.api.ServerHook;
14
import emu.grasscutter.scripts.ScriptLoader;
KingRainbow44's avatar
KingRainbow44 committed
15
import emu.grasscutter.utils.Utils;
16
17
18
19
20
21
import org.jline.reader.EndOfFileException;
import org.jline.reader.LineReader;
import org.jline.reader.LineReaderBuilder;
import org.jline.reader.UserInterruptException;
import org.jline.terminal.Terminal;
import org.jline.terminal.TerminalBuilder;
Melledy's avatar
Melledy committed
22
import org.reflections.Reflections;
Melledy's avatar
Melledy committed
23
24
25
26
27
28
29
30
import org.slf4j.LoggerFactory;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

import ch.qos.logback.classic.Logger;
import emu.grasscutter.data.ResourceLoader;
import emu.grasscutter.database.DatabaseManager;
Melledy's avatar
Melledy committed
31
32
import emu.grasscutter.languages.CNLanguage;
import emu.grasscutter.languages.Language;
Melledy's avatar
Melledy committed
33
34
35
36
37
import emu.grasscutter.server.dispatch.DispatchServer;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.Crypto;

KingRainbow44's avatar
KingRainbow44 committed
38
39
public final class Grasscutter {
	private static final Logger log = (Logger) LoggerFactory.getLogger(Grasscutter.class);
Melledy's avatar
Melledy committed
40
	private static Config config;
41
	private static LineReader consoleLineReader = null;
方块君's avatar
方块君 committed
42
	private static Language language;
mzfqy's avatar
mzfqy committed
43
	private static CNLanguage cn_language;
44

KingRainbow44's avatar
KingRainbow44 committed
45
46
	private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
	private static final File configFile = new File("./config.json");
Secretboy's avatar
Secretboy committed
47

48
	private static int day; // Current day of week
Secretboy's avatar
Secretboy committed
49

Melledy's avatar
Melledy committed
50
51
	private static DispatchServer dispatchServer;
	private static GameServer gameServer;
KingRainbow44's avatar
KingRainbow44 committed
52
	private static PluginManager pluginManager;
Secretboy's avatar
Secretboy committed
53

KingRainbow44's avatar
KingRainbow44 committed
54
	public static final Reflections reflector = new Reflections("emu.grasscutter");
Secretboy's avatar
Secretboy committed
55

KingRainbow44's avatar
KingRainbow44 committed
56
	static {
Melledy's avatar
Melledy committed
57
58
		// Declare logback configuration.
		System.setProperty("logback.configurationFile", "src/main/resources/logback.xml");
Secretboy's avatar
Secretboy committed
59

Melledy's avatar
Melledy committed
60
		// Load server configuration.
KingRainbow44's avatar
KingRainbow44 committed
61
		Grasscutter.loadConfig();
方块君's avatar
方块君 committed
62
63
64

		// Load Language
		Grasscutter.loadLanguage();
Secretboy's avatar
Secretboy committed
65

KingRainbow44's avatar
KingRainbow44 committed
66
67
68
		// Check server structure.
		Utils.startupCheck();
	}
Secretboy's avatar
Secretboy committed
69

Melledy's avatar
Melledy committed
70
71
    public static void main(String[] args) throws Exception {
    	Crypto.loadKeys();
Secretboy's avatar
Secretboy committed
72

Melledy's avatar
Melledy committed
73
74
		for (String arg : args) {
			switch (arg.toLowerCase()) {
KingRainbow44's avatar
KingRainbow44 committed
75
76
77
				case "-handbook" -> {
					Tools.createGmHandbook(); return;
				}
78
				case "-gachamap" -> {
79
					Tools.createGachaMapping("./gacha_mappings.js"); return;
80
				}
Melledy's avatar
Melledy committed
81
82
			}
		}
Secretboy's avatar
Secretboy committed
83

KingRainbow44's avatar
KingRainbow44 committed
84
		// Initialize server.
方块君's avatar
方块君 committed
85
		Grasscutter.getLogger().info(language.Starting_Grasscutter);
Secretboy's avatar
Secretboy committed
86

KingRainbow44's avatar
KingRainbow44 committed
87
		// Load all resources.
88
		Grasscutter.updateDayOfWeek();
Melledy's avatar
Melledy committed
89
		ResourceLoader.loadAll();
90
		ScriptLoader.init();
Secretboy's avatar
Secretboy committed
91

Melledy's avatar
Melledy committed
92
93
		// Database
		DatabaseManager.initialize();
KingRainbow44's avatar
KingRainbow44 committed
94

KingRainbow44's avatar
KingRainbow44 committed
95
96
		// Create plugin manager instance.
		pluginManager = new PluginManager();
Secretboy's avatar
Secretboy committed
97

KingRainbow44's avatar
KingRainbow44 committed
98
99
100
		// Create server instances.
		dispatchServer = new DispatchServer();
		gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port));
KingRainbow44's avatar
KingRainbow44 committed
101
102
		// Create a server hook instance with both servers.
		new ServerHook(gameServer, dispatchServer);
Secretboy's avatar
Secretboy committed
103

KingRainbow44's avatar
KingRainbow44 committed
104
		// Start servers.
105
		if (getConfig().RunMode == ServerRunMode.HYBRID) {
106
107
			dispatchServer.start();
			gameServer.start();
108
		} else if (getConfig().RunMode == ServerRunMode.DISPATCH_ONLY) {
109
			dispatchServer.start();
110
		} else if (getConfig().RunMode == ServerRunMode.GAME_ONLY) {
111
112
			gameServer.start();
		} else {
方块君's avatar
方块君 committed
113
114
115
			getLogger().error(language.Invalid_server_run_mode + " " + getConfig().RunMode);
			getLogger().error(language.Server_run_mode);
			getLogger().error(language.Shutting_down);
116
117
			System.exit(1);
		}
Secretboy's avatar
Secretboy committed
118

KingRainbow44's avatar
KingRainbow44 committed
119
120
		// Enable all plugins.
		pluginManager.enablePlugins();
121

KingRainbow44's avatar
KingRainbow44 committed
122
123
		// Hook into shutdown event.
		Runtime.getRuntime().addShutdownHook(new Thread(Grasscutter::onShutdown));
124
125
126

		// Open console.
		startConsole();
Melledy's avatar
Melledy committed
127
    }
KingRainbow44's avatar
KingRainbow44 committed
128
129
130
131
132
133
134
135

	/**
	 * Server shutdown event.
	 */
	private static void onShutdown() {
		// Disable all plugins.
		pluginManager.disablePlugins();
	}
136

Melledy's avatar
Melledy committed
137
138
139
	public static void loadConfig() {
		try (FileReader file = new FileReader(configFile)) {
			config = gson.fromJson(file, Config.class);
140
			saveConfig();
Melledy's avatar
Melledy committed
141
		} catch (Exception e) {
142
143
			Grasscutter.config = new Config(); 
			saveConfig();
Melledy's avatar
Melledy committed
144
145
		}
	}
方块君's avatar
方块君 committed
146
147

	public static void loadLanguage() {
Secretboy's avatar
Secretboy committed
148
		try (FileReader file = new FileReader(String.format("%s%s.json", getConfig().LANGUAGE_FOLDER, Grasscutter.config.LocaleLanguage))) {
方块君's avatar
方块君 committed
149
150
151
			language = gson.fromJson(file, Language.class);
		} catch (Exception e) {
			Grasscutter.language = new Language();
mzfqy's avatar
mzfqy committed
152
			Grasscutter.cn_language = new CNLanguage();
Secretboy's avatar
Secretboy committed
153
			Grasscutter.config.LocaleLanguage = Locale.getDefault();
方块君's avatar
方块君 committed
154
155
156
			saveConfig();

			try {
mzfqy's avatar
mzfqy committed
157
				File folder = new File("./languages");
方块君's avatar
方块君 committed
158
159
160
161
162
163
164
				if (!folder.exists() && !folder.isDirectory()) {
					//noinspection ResultOfMethodCallIgnored
					folder.mkdirs();
				}
			} catch (Exception ee) {
				Grasscutter.getLogger().error("Unable to create language folder.");
			}
Secretboy's avatar
Secretboy committed
165
			try (FileWriter file = new FileWriter("./languages/" + Locale.US + ".json")) {
方块君's avatar
方块君 committed
166
167
168
169
				file.write(gson.toJson(language));
			} catch (Exception ee) {
				Grasscutter.getLogger().error("Unable to create language file.");
			}
Secretboy's avatar
Secretboy committed
170
			try (FileWriter file = new FileWriter("./languages/" + Locale.SIMPLIFIED_CHINESE + ".json")) {
mzfqy's avatar
mzfqy committed
171
172
				file.write(gson.toJson(cn_language));
			} catch (Exception ee) {
Secretboy's avatar
Secretboy committed
173
174
175
176
177
178
179
180
				Grasscutter.getLogger().error("无法创建简体中文语言文件。");
			}

			// try again
			try (FileReader file = new FileReader(String.format("%s%s.json", getConfig().LANGUAGE_FOLDER, Grasscutter.config.LocaleLanguage))) {
				language = gson.fromJson(file, Language.class);
			} catch (Exception ee) {
				Grasscutter.getLogger().error("Unable to load " + Grasscutter.config.LocaleLanguage + ".json");
mzfqy's avatar
mzfqy committed
181
			}
方块君's avatar
方块君 committed
182
183
		}
	}
Secretboy's avatar
Secretboy committed
184

Melledy's avatar
Melledy committed
185
186
187
188
	public static void saveConfig() {
		try (FileWriter file = new FileWriter(configFile)) {
			file.write(gson.toJson(config));
		} catch (Exception e) {
KingRainbow44's avatar
KingRainbow44 committed
189
			Grasscutter.getLogger().error("Unable to save config file.");
Melledy's avatar
Melledy committed
190
191
		}
	}
Secretboy's avatar
Secretboy committed
192

Melledy's avatar
Melledy committed
193
	public static void startConsole() {
194
195
196
197
198
199
		// Console should not start in dispatch only mode.
		if (getConfig().RunMode == ServerRunMode.DISPATCH_ONLY) {
			getLogger().info(language.Dispatch_mode_not_support_command);
			return;
		}

方块君's avatar
方块君 committed
200
		getLogger().info(language.Start_done);
201
202
203
204
205
206
207
208
209
210
211
212
		String input = null;
		boolean isLastInterrupted = false;
		while (true) {
			try {
				input = consoleLineReader.readLine("> ");
			} catch (UserInterruptException e) {
				if (!isLastInterrupted) {
					isLastInterrupted = true;
					Grasscutter.getLogger().info("Press Ctrl-C again to shutdown.");
					continue;
				} else {
					Runtime.getRuntime().exit(0);
Melledy's avatar
Melledy committed
213
				}
214
215
216
217
218
219
220
221
222
223
			} catch (EndOfFileException e) {
				Grasscutter.getLogger().info("EOF detected.");
				continue;
			} catch (IOError e) {
				Grasscutter.getLogger().error("An IO error occurred.", e);
				continue;
			}

			isLastInterrupted = false;
			try {
AnimeGitB's avatar
AnimeGitB committed
224
				CommandMap.getInstance().invoke(null, null, input);
225
226
			} catch (Exception e) {
				Grasscutter.getLogger().error(language.Command_error, e);
Melledy's avatar
Melledy committed
227
228
229
			}
		}
	}
KingRainbow44's avatar
KingRainbow44 committed
230
231
232
233
234

	public static Config getConfig() {
		return config;
	}

方块君's avatar
方块君 committed
235
236
237
238
	public static Language getLanguage() {
		return language;
	}

KingRainbow44's avatar
KingRainbow44 committed
239
240
241
242
	public static Logger getLogger() {
		return log;
	}

243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
	public static LineReader getConsole() {
		if (consoleLineReader == null) {
			Terminal terminal = null;
			try {
				terminal = TerminalBuilder.builder().jna(true).build();
			} catch (Exception e) {
				try {
					// Fallback to a dumb jline terminal.
					terminal = TerminalBuilder.builder().dumb(true).build();
				} catch (Exception ignored) {
					// When dumb is true, build() never throws.
				}
			}
			consoleLineReader = LineReaderBuilder.builder()
					.terminal(terminal)
					.build();
		}
		return consoleLineReader;
	}

KingRainbow44's avatar
KingRainbow44 committed
263
264
265
266
267
268
269
270
271
272
273
	public static Gson getGsonFactory() {
		return gson;
	}

	public static DispatchServer getDispatchServer() {
		return dispatchServer;
	}

	public static GameServer getGameServer() {
		return gameServer;
	}
Secretboy's avatar
Secretboy committed
274

KingRainbow44's avatar
KingRainbow44 committed
275
276
277
	public static PluginManager getPluginManager() {
		return pluginManager;
	}
Secretboy's avatar
Secretboy committed
278

279
280
281
282
283
284
285
286
	public static void updateDayOfWeek() {
		Calendar calendar = Calendar.getInstance();
		day = calendar.get(Calendar.DAY_OF_WEEK); 
	}

	public static int getCurrentDayOfWeek() {
		return day;
	}
Secretboy's avatar
Secretboy committed
287

288
289
290
	public enum ServerRunMode {
		HYBRID, DISPATCH_ONLY, GAME_ONLY
	}
Secretboy's avatar
Secretboy committed
291

292
293
294
	public enum ServerDebugMode {
		ALL, MISSING, NONE
	}
Melledy's avatar
Melledy committed
295
}