Grasscutter.java 6.91 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;
7
import java.util.Calendar;
Melledy's avatar
Melledy committed
8

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

35
36
import static emu.grasscutter.utils.Language.translate;

KingRainbow44's avatar
KingRainbow44 committed
37
38
public final class Grasscutter {
	private static final Logger log = (Logger) LoggerFactory.getLogger(Grasscutter.class);
39
	private static LineReader consoleLineReader = null;
40
41

	private static Config config;
方块君's avatar
方块君 committed
42
	private static Language language;
43

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

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

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

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

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

62
		// Load translation files.
方块君's avatar
方块君 committed
63
		Grasscutter.loadLanguage();
Secretboy's avatar
Secretboy committed
64

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

Magix's avatar
Magix committed
69
  public static void main(String[] args) throws Exception {
70
    	Crypto.loadKeys(); // Load keys from buffers.
Secretboy's avatar
Secretboy committed
71

72
		// Parse arguments.
KingRainbow44's avatar
KingRainbow44 committed
73
		boolean exitEarly = false;
Melledy's avatar
Melledy committed
74
75
		for (String arg : args) {
			switch (arg.toLowerCase()) {
KingRainbow44's avatar
KingRainbow44 committed
76
				case "-handbook" -> {
KingRainbow44's avatar
KingRainbow44 committed
77
					Tools.createGmHandbook(); exitEarly = true;
KingRainbow44's avatar
KingRainbow44 committed
78
				}
79
				case "-gachamap" -> {
KingRainbow44's avatar
KingRainbow44 committed
80
					Tools.createGachaMapping("./gacha-mapping.js"); exitEarly = true;
81
				}
Melledy's avatar
Melledy committed
82
			}
KingRainbow44's avatar
KingRainbow44 committed
83
84
85
86
		} 
		
		// Exit early if argument sets it.
		if(exitEarly) System.exit(0);
Secretboy's avatar
Secretboy committed
87

KingRainbow44's avatar
KingRainbow44 committed
88
		// Initialize server.
89
		Grasscutter.getLogger().info(translate("messages.status.starting"));
Secretboy's avatar
Secretboy committed
90

KingRainbow44's avatar
KingRainbow44 committed
91
		// Load all resources.
92
		Grasscutter.updateDayOfWeek();
Melledy's avatar
Melledy committed
93
		ResourceLoader.loadAll();
94
		ScriptLoader.init();
Secretboy's avatar
Secretboy committed
95

KingRainbow44's avatar
KingRainbow44 committed
96
		// Initialize database.
Melledy's avatar
Melledy committed
97
		DatabaseManager.initialize();
KingRainbow44's avatar
KingRainbow44 committed
98
99
100

		// Create server instances.
		dispatchServer = new DispatchServer();
101
		gameServer = new GameServer();
KingRainbow44's avatar
KingRainbow44 committed
102
103
		// Create a server hook instance with both servers.
		new ServerHook(gameServer, dispatchServer);
104
105
		// Create plugin manager instance.
		pluginManager = new PluginManager();
Secretboy's avatar
Secretboy committed
106

KingRainbow44's avatar
KingRainbow44 committed
107
		// Start servers.
108
		if (getConfig().RunMode == ServerRunMode.HYBRID) {
109
110
			dispatchServer.start();
			gameServer.start();
111
		} else if (getConfig().RunMode == ServerRunMode.DISPATCH_ONLY) {
112
			dispatchServer.start();
113
		} else if (getConfig().RunMode == ServerRunMode.GAME_ONLY) {
114
115
			gameServer.start();
		} else {
116
117
118
			getLogger().error(translate("messages.status.run_mode_error", getConfig().RunMode));
			getLogger().error(translate("messages.status.run_mode_help"));
			getLogger().error(translate("messages.status.shutdown"));
119
120
			System.exit(1);
		}
Secretboy's avatar
Secretboy committed
121

KingRainbow44's avatar
KingRainbow44 committed
122
123
		// Enable all plugins.
		pluginManager.enablePlugins();
124

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

		// Open console.
		startConsole();
Magix's avatar
Magix committed
130
 }
KingRainbow44's avatar
KingRainbow44 committed
131
132
133
134
135
136
137
138

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

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

	public static void loadLanguage() {
151
152
		var locale = config.LocaleLanguage;
		language = Language.getLanguage(locale.toLanguageTag());
方块君's avatar
方块君 committed
153
	}
Secretboy's avatar
Secretboy committed
154

Melledy's avatar
Melledy committed
155
156
157
158
	public static void saveConfig() {
		try (FileWriter file = new FileWriter(configFile)) {
			file.write(gson.toJson(config));
		} catch (Exception e) {
KingRainbow44's avatar
KingRainbow44 committed
159
			Grasscutter.getLogger().error("Unable to save config file.");
Melledy's avatar
Melledy committed
160
161
		}
	}
Secretboy's avatar
Secretboy committed
162

Melledy's avatar
Melledy committed
163
	public static void startConsole() {
164
165
		// Console should not start in dispatch only mode.
		if (getConfig().RunMode == ServerRunMode.DISPATCH_ONLY) {
166
			getLogger().info(translate("messages.dispatch.no_commands_error"));
167
168
169
			return;
		}

170
		getLogger().info(translate("messages.status.done"));
171
172
173
174
175
176
177
178
179
180
181
182
		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
183
				}
184
185
186
187
188
189
190
191
192
193
			} 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
194
				CommandMap.getInstance().invoke(null, null, input);
195
			} catch (Exception e) {
196
				Grasscutter.getLogger().error(translate("messages.game.command_error"), e);
Melledy's avatar
Melledy committed
197
198
199
			}
		}
	}
KingRainbow44's avatar
KingRainbow44 committed
200
201
202
203
204

	public static Config getConfig() {
		return config;
	}

方块君's avatar
方块君 committed
205
206
207
208
	public static Language getLanguage() {
		return language;
	}

KingRainbow44's avatar
KingRainbow44 committed
209
210
211
212
	public static Logger getLogger() {
		return log;
	}

213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
	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
233
234
235
236
237
238
239
240
241
242
243
	public static Gson getGsonFactory() {
		return gson;
	}

	public static DispatchServer getDispatchServer() {
		return dispatchServer;
	}

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

KingRainbow44's avatar
KingRainbow44 committed
245
246
247
	public static PluginManager getPluginManager() {
		return pluginManager;
	}
Secretboy's avatar
Secretboy committed
248

249
250
251
252
253
254
255
256
	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
257

258
259
260
	public enum ServerRunMode {
		HYBRID, DISPATCH_ONLY, GAME_ONLY
	}
Secretboy's avatar
Secretboy committed
261

262
263
264
	public enum ServerDebugMode {
		ALL, MISSING, NONE
	}
Melledy's avatar
Melledy committed
265
}