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

3
import java.io.*;
4
import java.util.Calendar;
Melledy's avatar
Melledy committed
5

Jaida Wu's avatar
Jaida Wu committed
6
import emu.grasscutter.command.CommandMap;
gentlespoon's avatar
gentlespoon committed
7
import emu.grasscutter.game.managers.StaminaManager.StaminaManager;
KingRainbow44's avatar
KingRainbow44 committed
8
import emu.grasscutter.plugin.PluginManager;
KingRainbow44's avatar
KingRainbow44 committed
9
import emu.grasscutter.plugin.api.ServerHook;
10
import emu.grasscutter.scripts.ScriptLoader;
11
import emu.grasscutter.utils.ConfigContainer;
KingRainbow44's avatar
KingRainbow44 committed
12
import emu.grasscutter.utils.Utils;
13
14
15
16
17
18
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
19
import org.reflections.Reflections;
Melledy's avatar
Melledy committed
20
21
22
23
24
25
26
27
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;
28
import emu.grasscutter.utils.Language;
Melledy's avatar
Melledy committed
29
30
31
32
import emu.grasscutter.server.dispatch.DispatchServer;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.Crypto;
33
import emu.grasscutter.BuildConfig;
Melledy's avatar
Melledy committed
34

35
36
import javax.annotation.Nullable;

37
import static emu.grasscutter.utils.Language.translate;
38
import static emu.grasscutter.Configuration.*;
39

KingRainbow44's avatar
KingRainbow44 committed
40
41
public final class Grasscutter {
	private static final Logger log = (Logger) LoggerFactory.getLogger(Grasscutter.class);
42
	private static LineReader consoleLineReader = null;
43
	
方块君's avatar
方块君 committed
44
	private static Language language;
45

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

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

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

KingRainbow44's avatar
KingRainbow44 committed
55
	public static final Reflections reflector = new Reflections("emu.grasscutter");
56
	public static ConfigContainer config;
Magix's avatar
Magix committed
57
  
KingRainbow44's avatar
KingRainbow44 committed
58
	static {
Melledy's avatar
Melledy committed
59
60
		// Declare logback configuration.
		System.setProperty("logback.configurationFile", "src/main/resources/logback.xml");
Secretboy's avatar
Secretboy committed
61

Melledy's avatar
Melledy committed
62
		// Load server configuration.
63
		Grasscutter.loadConfig();
64
		// Attempt to update configuration.
65
		ConfigContainer.updateConfig();
方块君's avatar
方块君 committed
66

67
		// Load translation files.
方块君's avatar
方块君 committed
68
		Grasscutter.loadLanguage();
Secretboy's avatar
Secretboy committed
69

KingRainbow44's avatar
KingRainbow44 committed
70
71
72
		// Check server structure.
		Utils.startupCheck();
	}
Secretboy's avatar
Secretboy committed
73

74
75
76
  	public static void main(String[] args) throws Exception {
		Crypto.loadKeys(); // Load keys from buffers.
	
77
		// Parse arguments.
KingRainbow44's avatar
KingRainbow44 committed
78
		boolean exitEarly = false;
Melledy's avatar
Melledy committed
79
80
		for (String arg : args) {
			switch (arg.toLowerCase()) {
KingRainbow44's avatar
KingRainbow44 committed
81
				case "-handbook" -> {
KingRainbow44's avatar
KingRainbow44 committed
82
					Tools.createGmHandbook(); exitEarly = true;
KingRainbow44's avatar
KingRainbow44 committed
83
				}
84
				case "-gachamap" -> {
85
					Tools.createGachaMapping(DATA("gacha_mappings.js")); exitEarly = true;
86
				}
87
				case "-version" -> {
mingjun97's avatar
mingjun97 committed
88
					System.out.println("Grasscutter version: " + BuildConfig.VERSION + "-" + BuildConfig.GIT_HASH); exitEarly = true;
89
				}
Melledy's avatar
Melledy committed
90
			}
KingRainbow44's avatar
KingRainbow44 committed
91
92
93
94
		} 
		
		// Exit early if argument sets it.
		if(exitEarly) System.exit(0);
95
	
KingRainbow44's avatar
KingRainbow44 committed
96
		// Initialize server.
97
		Grasscutter.getLogger().info(translate("messages.status.starting"));
98
	
KingRainbow44's avatar
KingRainbow44 committed
99
		// Load all resources.
100
		Grasscutter.updateDayOfWeek();
Melledy's avatar
Melledy committed
101
		ResourceLoader.loadAll();
102
		ScriptLoader.init();
103
	
KingRainbow44's avatar
KingRainbow44 committed
104
		// Initialize database.
Melledy's avatar
Melledy committed
105
		DatabaseManager.initialize();
106
	
KingRainbow44's avatar
KingRainbow44 committed
107
108
		// Create server instances.
		dispatchServer = new DispatchServer();
109
		gameServer = new GameServer();
KingRainbow44's avatar
KingRainbow44 committed
110
111
		// Create a server hook instance with both servers.
		new ServerHook(gameServer, dispatchServer);
112
113
		// Create plugin manager instance.
		pluginManager = new PluginManager();
gentlespoon's avatar
gentlespoon committed
114
115
116

		// TODO: find a better place?
		StaminaManager.initialize();
117
	
KingRainbow44's avatar
KingRainbow44 committed
118
		// Start servers.
119
120
		var runMode = SERVER.runMode;
		if (runMode == ServerRunMode.HYBRID) {
121
122
			dispatchServer.start();
			gameServer.start();
123
		} else if (runMode == ServerRunMode.DISPATCH_ONLY) {
124
			dispatchServer.start();
125
		} else if (runMode == ServerRunMode.GAME_ONLY) {
126
127
			gameServer.start();
		} else {
128
			getLogger().error(translate("messages.status.run_mode_error", runMode));
129
130
			getLogger().error(translate("messages.status.run_mode_help"));
			getLogger().error(translate("messages.status.shutdown"));
131
132
			System.exit(1);
		}
133
	
KingRainbow44's avatar
KingRainbow44 committed
134
135
		// Enable all plugins.
		pluginManager.enablePlugins();
136
	
KingRainbow44's avatar
KingRainbow44 committed
137
138
		// Hook into shutdown event.
		Runtime.getRuntime().addShutdownHook(new Thread(Grasscutter::onShutdown));
139
	
140
141
		// Open console.
		startConsole();
142
 	}
KingRainbow44's avatar
KingRainbow44 committed
143
144
145
146
147
148
149
150

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

152
153
154
	/**
	 * Attempts to load the configuration from a file.
	 */
155
	public static void loadConfig() {
Melledy's avatar
Melledy committed
156
		try (FileReader file = new FileReader(configFile)) {
157
158
159
160
161
162
			config = gson.fromJson(file, ConfigContainer.class);
		} catch (Exception exception) {
			Grasscutter.saveConfig(null);
			config = new ConfigContainer();
		} catch (Error error) {
			// Occurred probably from an outdated config file.
163
			Grasscutter.saveConfig(null);
164
			config = new ConfigContainer();
Melledy's avatar
Melledy committed
165
166
		}
	}
方块君's avatar
方块君 committed
167
168

	public static void loadLanguage() {
169
		var locale = config.language.language;
Secretboy-SMR's avatar
Secretboy-SMR committed
170
        language = Language.getLanguage(Utils.getLanguageCode(locale));
方块君's avatar
方块君 committed
171
	}
Secretboy's avatar
Secretboy committed
172

173
174
175
176
	/**
	 * Saves the provided server configuration.
	 * @param config The configuration to save, or null for a new one.
	 */
177
178
	public static void saveConfig(@Nullable ConfigContainer config) {
		if(config == null) config = new ConfigContainer();
179
		
Melledy's avatar
Melledy committed
180
181
		try (FileWriter file = new FileWriter(configFile)) {
			file.write(gson.toJson(config));
182
183
		} catch (IOException ignored) {
			Grasscutter.getLogger().error("Unable to write to config file.");
Melledy's avatar
Melledy committed
184
		} catch (Exception e) {
185
			Grasscutter.getLogger().error("Unable to save config file.", e);
Melledy's avatar
Melledy committed
186
187
		}
	}
Secretboy's avatar
Secretboy committed
188

Melledy's avatar
Melledy committed
189
	public static void startConsole() {
190
		// Console should not start in dispatch only mode.
191
		if (SERVER.runMode == ServerRunMode.DISPATCH_ONLY) {
192
			getLogger().info(translate("messages.dispatch.no_commands_error"));
193
194
195
			return;
		}

196
		getLogger().info(translate("messages.status.done"));
197
		getLogger().info(translate("messages.status.version", BuildConfig.VERSION, BuildConfig.GIT_HASH));
198
199
200
201
202
203
204
205
206
207
208
209
		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
210
				}
211
212
213
214
215
216
217
218
219
220
			} 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
221
				CommandMap.getInstance().invoke(null, null, input);
222
			} catch (Exception e) {
223
				Grasscutter.getLogger().error(translate("messages.game.command_error"), e);
Melledy's avatar
Melledy committed
224
225
226
			}
		}
	}
KingRainbow44's avatar
KingRainbow44 committed
227

228
	public static ConfigContainer getConfig() {
KingRainbow44's avatar
KingRainbow44 committed
229
230
231
		return config;
	}

方块君's avatar
方块君 committed
232
233
234
235
	public static Language getLanguage() {
		return language;
	}

Secretboy's avatar
Secretboy committed
236
237
238
239
240
241
242
243
	public static void setLanguage(Language language) {
        Grasscutter.language = language;
	}

	public static Language getLanguage(String langCode) {
        return Language.getLanguage(langCode);
	}

KingRainbow44's avatar
KingRainbow44 committed
244
245
246
247
	public static Logger getLogger() {
		return log;
	}

248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
	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
268
269
270
271
272
273
274
275
276
277
278
	public static Gson getGsonFactory() {
		return gson;
	}

	public static DispatchServer getDispatchServer() {
		return dispatchServer;
	}

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

KingRainbow44's avatar
KingRainbow44 committed
280
281
282
	public static PluginManager getPluginManager() {
		return pluginManager;
	}
Secretboy's avatar
Secretboy committed
283

284
285
286
287
288
289
290
291
	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
292

293
294
295
	public enum ServerRunMode {
		HYBRID, DISPATCH_ONLY, GAME_ONLY
	}
Secretboy's avatar
Secretboy committed
296

297
298
299
	public enum ServerDebugMode {
		ALL, MISSING, NONE
	}
Melledy's avatar
Melledy committed
300
}