2022-04-17 12:43:07 +00:00
package emu.grasscutter ;
2022-05-11 04:30:07 +00:00
import java.io.* ;
2022-04-29 21:29:34 +00:00
import java.util.Calendar ;
2022-04-17 12:43:07 +00:00
2022-05-13 15:39:40 +00:00
import emu.grasscutter.auth.AuthenticationSystem ;
import emu.grasscutter.auth.DefaultAuthentication ;
2022-04-20 12:21:38 +00:00
import emu.grasscutter.command.CommandMap ;
2022-06-16 14:57:57 +00:00
import emu.grasscutter.game.managers.energy.EnergyManager ;
import emu.grasscutter.game.managers.stamina.StaminaManager ;
2022-04-23 05:17:35 +00:00
import emu.grasscutter.plugin.PluginManager ;
2022-05-03 01:20:24 +00:00
import emu.grasscutter.plugin.api.ServerHook ;
2022-04-29 05:19:14 +00:00
import emu.grasscutter.scripts.ScriptLoader ;
2022-05-13 15:39:40 +00:00
import emu.grasscutter.server.http.HttpServer ;
import emu.grasscutter.server.http.dispatch.DispatchHandler ;
2022-05-14 03:22:30 +00:00
import emu.grasscutter.server.http.handlers.* ;
2022-05-13 15:39:40 +00:00
import emu.grasscutter.server.http.dispatch.RegionHandler ;
2022-05-15 15:04:00 +00:00
import emu.grasscutter.server.http.documentation.DocumentationServerHandler ;
2022-05-11 05:23:18 +00:00
import emu.grasscutter.utils.ConfigContainer ;
2022-04-18 05:11:27 +00:00
import emu.grasscutter.utils.Utils ;
2022-05-03 12:44:52 +00:00
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 ;
2022-04-19 04:35:01 +00:00
import org.reflections.Reflections ;
2022-04-17 12:43:07 +00:00
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 ;
2022-05-06 03:57:55 +00:00
import emu.grasscutter.utils.Language ;
2022-04-17 12:43:07 +00:00
import emu.grasscutter.server.game.GameServer ;
import emu.grasscutter.tools.Tools ;
import emu.grasscutter.utils.Crypto ;
2022-05-11 04:30:07 +00:00
import javax.annotation.Nullable ;
2022-05-06 03:57:55 +00:00
import static emu.grasscutter.utils.Language.translate ;
2022-05-11 04:30:07 +00:00
import static emu.grasscutter.Configuration.* ;
2022-05-06 03:57:55 +00:00
2022-04-18 05:11:27 +00:00
public final class Grasscutter {
private static final Logger log = ( Logger ) LoggerFactory . getLogger ( Grasscutter . class ) ;
2022-05-03 12:44:52 +00:00
private static LineReader consoleLineReader = null ;
2022-05-11 04:30:07 +00:00
2022-05-03 06:23:25 +00:00
private static Language language ;
2022-05-03 12:44:52 +00:00
2022-04-18 05:11:27 +00:00
private static final Gson gson = new GsonBuilder ( ) . setPrettyPrinting ( ) . create ( ) ;
2022-05-11 04:46:49 +00:00
public static final File configFile = new File ( " ./config.json " ) ;
2022-05-05 05:24:49 +00:00
2022-05-06 03:57:55 +00:00
private static int day ; // Current day of week.
2022-05-05 05:24:49 +00:00
2022-05-13 15:39:40 +00:00
private static HttpServer httpServer ;
2022-04-17 12:43:07 +00:00
private static GameServer gameServer ;
2022-04-23 05:17:35 +00:00
private static PluginManager pluginManager ;
2022-05-13 15:39:40 +00:00
private static AuthenticationSystem authenticationSystem ;
2022-05-05 05:24:49 +00:00
2022-04-26 04:39:05 +00:00
public static final Reflections reflector = new Reflections ( " emu.grasscutter " ) ;
2022-05-11 05:23:18 +00:00
public static ConfigContainer config ;
2022-05-07 01:39:30 +00:00
2022-04-18 05:11:27 +00:00
static {
2022-04-19 04:35:01 +00:00
// Declare logback configuration.
System . setProperty ( " logback.configurationFile " , " src/main/resources/logback.xml " ) ;
2022-05-05 05:24:49 +00:00
2022-04-19 04:35:01 +00:00
// Load server configuration.
2022-05-11 05:23:18 +00:00
Grasscutter . loadConfig ( ) ;
2022-05-11 04:46:49 +00:00
// Attempt to update configuration.
2022-05-11 05:23:18 +00:00
ConfigContainer . updateConfig ( ) ;
2022-05-03 06:23:25 +00:00
2022-05-06 03:57:55 +00:00
// Load translation files.
2022-05-03 06:23:25 +00:00
Grasscutter . loadLanguage ( ) ;
2022-05-05 05:24:49 +00:00
2022-04-18 05:11:27 +00:00
// Check server structure.
Utils . startupCheck ( ) ;
}
2022-05-05 05:24:49 +00:00
2022-05-11 04:30:07 +00:00
public static void main ( String [ ] args ) throws Exception {
Crypto . loadKeys ( ) ; // Load keys from buffers.
2022-05-06 03:57:55 +00:00
// Parse arguments.
2022-05-07 01:04:39 +00:00
boolean exitEarly = false ;
2022-04-17 12:43:07 +00:00
for ( String arg : args ) {
switch ( arg . toLowerCase ( ) ) {
2022-04-23 05:17:35 +00:00
case " -handbook " - > {
2022-05-07 01:04:39 +00:00
Tools . createGmHandbook ( ) ; exitEarly = true ;
2022-04-23 05:17:35 +00:00
}
2022-05-02 06:17:18 +00:00
case " -gachamap " - > {
2022-05-11 04:30:07 +00:00
Tools . createGachaMapping ( DATA ( " gacha_mappings.js " ) ) ; exitEarly = true ;
2022-05-02 06:17:18 +00:00
}
2022-05-11 01:34:30 +00:00
case " -version " - > {
2022-05-11 06:23:58 +00:00
System . out . println ( " Grasscutter version: " + BuildConfig . VERSION + " - " + BuildConfig . GIT_HASH ) ; exitEarly = true ;
2022-05-11 01:34:30 +00:00
}
2022-04-17 12:43:07 +00:00
}
2022-05-07 01:04:39 +00:00
}
// Exit early if argument sets it.
if ( exitEarly ) System . exit ( 0 ) ;
2022-05-11 04:30:07 +00:00
2022-04-18 05:11:27 +00:00
// Initialize server.
2022-05-06 03:57:55 +00:00
Grasscutter . getLogger ( ) . info ( translate ( " messages.status.starting " ) ) ;
2022-06-04 09:41:18 +00:00
Grasscutter . getLogger ( ) . info ( translate ( " messages.status.game_version " , GameConstants . VERSION ) ) ;
Grasscutter . getLogger ( ) . info ( translate ( " messages.status.version " , BuildConfig . VERSION , BuildConfig . GIT_HASH ) ) ;
2022-05-11 04:30:07 +00:00
2022-04-18 05:11:27 +00:00
// Load all resources.
2022-04-29 21:29:34 +00:00
Grasscutter . updateDayOfWeek ( ) ;
2022-04-17 12:43:07 +00:00
ResourceLoader . loadAll ( ) ;
2022-04-29 05:19:14 +00:00
ScriptLoader . init ( ) ;
2022-05-25 04:43:04 +00:00
EnergyManager . initialize ( ) ;
2022-05-11 04:30:07 +00:00
2022-05-07 01:52:10 +00:00
// Initialize database.
2022-04-17 12:43:07 +00:00
DatabaseManager . initialize ( ) ;
2022-05-13 15:39:40 +00:00
// Initialize the default authentication system.
authenticationSystem = new DefaultAuthentication ( ) ;
2022-05-11 04:30:07 +00:00
2022-04-23 05:17:35 +00:00
// Create server instances.
2022-05-13 15:39:40 +00:00
httpServer = new HttpServer ( ) ;
2022-05-06 03:57:55 +00:00
gameServer = new GameServer ( ) ;
2022-05-03 01:20:24 +00:00
// Create a server hook instance with both servers.
2022-05-14 03:22:30 +00:00
new ServerHook ( gameServer , httpServer ) ;
2022-05-13 15:39:40 +00:00
2022-05-06 10:16:07 +00:00
// Create plugin manager instance.
pluginManager = new PluginManager ( ) ;
2022-05-13 15:39:40 +00:00
// Add HTTP routes after loading plugins.
httpServer . addRouter ( HttpServer . UnhandledRequestRouter . class ) ;
httpServer . addRouter ( HttpServer . DefaultRequestRouter . class ) ;
httpServer . addRouter ( RegionHandler . class ) ;
httpServer . addRouter ( LogHandler . class ) ;
httpServer . addRouter ( GenericHandler . class ) ;
httpServer . addRouter ( AnnouncementsHandler . class ) ;
httpServer . addRouter ( DispatchHandler . class ) ;
2022-05-14 03:22:30 +00:00
httpServer . addRouter ( GachaHandler . class ) ;
2022-05-15 15:04:00 +00:00
httpServer . addRouter ( DocumentationServerHandler . class ) ;
2022-05-14 16:08:33 +00:00
2022-05-11 14:22:19 +00:00
// TODO: find a better place?
StaminaManager . initialize ( ) ;
2022-05-11 04:30:07 +00:00
2022-04-18 05:11:27 +00:00
// Start servers.
2022-05-11 04:30:07 +00:00
var runMode = SERVER . runMode ;
if ( runMode = = ServerRunMode . HYBRID ) {
2022-05-14 03:22:30 +00:00
httpServer . start ( ) ;
2022-04-21 10:04:00 +00:00
gameServer . start ( ) ;
2022-05-11 04:30:07 +00:00
} else if ( runMode = = ServerRunMode . DISPATCH_ONLY ) {
2022-05-13 15:39:40 +00:00
httpServer . start ( ) ;
2022-05-11 04:30:07 +00:00
} else if ( runMode = = ServerRunMode . GAME_ONLY ) {
2022-04-21 10:04:00 +00:00
gameServer . start ( ) ;
} else {
2022-05-11 04:30:07 +00:00
getLogger ( ) . error ( translate ( " messages.status.run_mode_error " , runMode ) ) ;
2022-05-06 03:57:55 +00:00
getLogger ( ) . error ( translate ( " messages.status.run_mode_help " ) ) ;
getLogger ( ) . error ( translate ( " messages.status.shutdown " ) ) ;
2022-04-21 10:04:00 +00:00
System . exit ( 1 ) ;
}
2022-05-11 04:30:07 +00:00
2022-04-23 05:17:35 +00:00
// Enable all plugins.
pluginManager . enablePlugins ( ) ;
2022-05-11 04:30:07 +00:00
2022-04-23 05:17:35 +00:00
// Hook into shutdown event.
Runtime . getRuntime ( ) . addShutdownHook ( new Thread ( Grasscutter : : onShutdown ) ) ;
2022-05-11 04:30:07 +00:00
2022-05-03 12:44:52 +00:00
// Open console.
startConsole ( ) ;
2022-05-11 04:30:07 +00:00
}
2022-04-23 05:17:35 +00:00
/ * *
* Server shutdown event .
* /
private static void onShutdown ( ) {
// Disable all plugins.
pluginManager . disablePlugins ( ) ;
}
2022-05-03 12:44:52 +00:00
2022-05-13 15:39:40 +00:00
/ *
* Methods for the language system component .
* /
public static void loadLanguage ( ) {
var locale = config . language . language ;
language = Language . getLanguage ( Utils . getLanguageCode ( locale ) ) ;
}
/ *
* Methods for the configuration system component .
* /
2022-05-11 04:30:07 +00:00
/ * *
* Attempts to load the configuration from a file .
* /
2022-05-11 05:23:18 +00:00
public static void loadConfig ( ) {
2022-05-13 12:10:02 +00:00
// Check if config.json exists. If not, we generate a new config.
if ( ! configFile . exists ( ) ) {
getLogger ( ) . info ( " config.json could not be found. Generating a default configuration ... " ) ;
config = new ConfigContainer ( ) ;
Grasscutter . saveConfig ( config ) ;
return ;
}
// If the file already exists, we attempt to load it.
2022-04-17 12:43:07 +00:00
try ( FileReader file = new FileReader ( configFile ) ) {
2022-05-11 05:23:18 +00:00
config = gson . fromJson ( file , ConfigContainer . class ) ;
} catch ( Exception exception ) {
2022-05-13 12:10:02 +00:00
getLogger ( ) . error ( " There was an error while trying to load the configuration from config.json. Please make sure that there are no syntax errors. If you want to start with a default configuration, delete your existing config.json. " ) ;
System . exit ( 1 ) ;
}
2022-04-17 12:43:07 +00:00
}
2022-05-03 06:23:25 +00:00
2022-05-11 04:30:07 +00:00
/ * *
* Saves the provided server configuration .
* @param config The configuration to save , or null for a new one .
* /
2022-05-11 05:23:18 +00:00
public static void saveConfig ( @Nullable ConfigContainer config ) {
if ( config = = null ) config = new ConfigContainer ( ) ;
2022-05-11 04:30:07 +00:00
2022-04-17 12:43:07 +00:00
try ( FileWriter file = new FileWriter ( configFile ) ) {
file . write ( gson . toJson ( config ) ) ;
2022-05-11 04:30:07 +00:00
} catch ( IOException ignored ) {
Grasscutter . getLogger ( ) . error ( " Unable to write to config file. " ) ;
2022-04-17 12:43:07 +00:00
} catch ( Exception e ) {
2022-05-11 04:30:07 +00:00
Grasscutter . getLogger ( ) . error ( " Unable to save config file. " , e ) ;
2022-04-17 12:43:07 +00:00
}
}
2022-05-05 05:24:49 +00:00
2022-05-13 15:39:40 +00:00
/ *
* Getters for the various server components .
* /
2022-05-11 05:23:18 +00:00
public static ConfigContainer getConfig ( ) {
2022-04-18 05:11:27 +00:00
return config ;
}
2022-05-03 06:23:25 +00:00
public static Language getLanguage ( ) {
return language ;
}
2022-05-10 12:33:45 +00:00
public static void setLanguage ( Language language ) {
Grasscutter . language = language ;
}
public static Language getLanguage ( String langCode ) {
return Language . getLanguage ( langCode ) ;
}
2022-04-18 05:11:27 +00:00
public static Logger getLogger ( ) {
return log ;
}
2022-05-03 12:44:52 +00:00
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 ;
}
2022-04-18 05:11:27 +00:00
public static Gson getGsonFactory ( ) {
return gson ;
}
2022-05-14 03:22:30 +00:00
public static HttpServer getHttpServer ( ) {
return httpServer ;
2022-04-18 05:11:27 +00:00
}
public static GameServer getGameServer ( ) {
return gameServer ;
}
2022-05-05 05:24:49 +00:00
2022-04-23 05:17:35 +00:00
public static PluginManager getPluginManager ( ) {
return pluginManager ;
}
2022-05-13 15:39:40 +00:00
public static AuthenticationSystem getAuthenticationSystem ( ) {
return authenticationSystem ;
}
2022-05-05 05:24:49 +00:00
2022-05-13 15:39:40 +00:00
public static int getCurrentDayOfWeek ( ) {
return day ;
}
/ *
* Utility methods .
* /
2022-04-29 21:29:34 +00:00
public static void updateDayOfWeek ( ) {
Calendar calendar = Calendar . getInstance ( ) ;
day = calendar . get ( Calendar . DAY_OF_WEEK ) ;
}
2022-05-13 15:39:40 +00:00
public static void startConsole ( ) {
// Console should not start in dispatch only mode.
if ( SERVER . runMode = = ServerRunMode . DISPATCH_ONLY ) {
getLogger ( ) . info ( translate ( " messages.dispatch.no_commands_error " ) ) ;
return ;
}
getLogger ( ) . info ( translate ( " messages.status.done " ) ) ;
String input = null ;
boolean isLastInterrupted = false ;
2022-05-17 00:29:23 +00:00
while ( config . server . game . enableConsole ) {
2022-05-13 15:39:40 +00:00
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 ) ;
}
} catch ( EndOfFileException e ) {
Grasscutter . getLogger ( ) . info ( " EOF detected. " ) ;
continue ;
} catch ( IOError e ) {
Grasscutter . getLogger ( ) . error ( " An IO error occurred. " , e ) ;
continue ;
}
isLastInterrupted = false ;
try {
CommandMap . getInstance ( ) . invoke ( null , null , input ) ;
} catch ( Exception e ) {
Grasscutter . getLogger ( ) . error ( translate ( " messages.game.command_error " ) , e ) ;
}
}
2022-04-29 21:29:34 +00:00
}
2022-05-05 05:24:49 +00:00
2022-05-13 15:39:40 +00:00
/ * *
* Sets the authentication system for the server .
* @param authenticationSystem The authentication system to use .
* /
public static void setAuthenticationSystem ( AuthenticationSystem authenticationSystem ) {
Grasscutter . authenticationSystem = authenticationSystem ;
}
/ *
* Enums for the configuration .
* /
2022-05-01 05:52:09 +00:00
public enum ServerRunMode {
HYBRID , DISPATCH_ONLY , GAME_ONLY
}
2022-05-05 05:24:49 +00:00
2022-05-01 05:52:09 +00:00
public enum ServerDebugMode {
ALL , MISSING , NONE
}
2022-04-17 12:43:07 +00:00
}