Compare commits
No commits in common. "develop" and "v1-alpha6" have entirely different histories.
140 changed files with 5852 additions and 4706 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -42,6 +42,3 @@ bin/
|
|||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
|
||||
### Java ###
|
||||
hs_err_pid*.log
|
||||
|
|
|
@ -26,6 +26,7 @@ import de.staropensource.engine.base.type.logging.LogLevel;
|
|||
import org.fusesource.jansi.Ansi;
|
||||
import org.fusesource.jansi.AnsiConsole;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Prints colored log output using the Jansi library.
|
||||
|
@ -42,16 +43,28 @@ public class AnsiLoggingAdapter implements LoggingAdapter {
|
|||
*/
|
||||
public AnsiLoggingAdapter() {}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String prePlaceholder(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format) {
|
||||
return format; // No modifications necessary
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String postPlaceholder(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format) {
|
||||
return format; // No modifications necessary
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
@SuppressWarnings({ "resource" }) // Using try-with-resources will cause issues here
|
||||
public void print(@NotNull LogLevel level, @NotNull StackTraceElement issuer, @NotNull String message, @NotNull String format) {
|
||||
public void print(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format) {
|
||||
// Convert to Ansi
|
||||
Ansi output = new AnsiShortcodeParser(format, true).getAnsi();
|
||||
Ansi output = new AnsiShortcodeConverter(format, true).getAnsi();
|
||||
|
||||
// Print message
|
||||
if (level == LogLevel.ERROR || level == LogLevel.CRASH)
|
||||
if (EngineConfiguration.getInstance().isLogForceStandardOutput())
|
||||
if (EngineConfiguration.getInstance().isLoggerForceStandardOutput())
|
||||
AnsiConsole.out().println(output);
|
||||
else
|
||||
AnsiConsole.err().println(output);
|
||||
|
|
|
@ -28,13 +28,12 @@ import java.util.HashSet;
|
|||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Implementation of the {@link ShortcodeParser} class
|
||||
* with ANSI support using the Jansi library.
|
||||
* Converts shortcodes such as {@code <bold>} or {@code <blink>} into a usable {@link Ansi} escape sequence.
|
||||
*
|
||||
* @see ShortcodeParser
|
||||
* @since v1-alpha8
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public final class AnsiShortcodeParser extends ShortcodeParser {
|
||||
public final class AnsiShortcodeConverter extends ShortcodeParser {
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
|
@ -43,7 +42,7 @@ public final class AnsiShortcodeParser extends ShortcodeParser {
|
|||
* @throws ParserException when parsing failed
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public AnsiShortcodeParser(@NotNull String string, boolean ignoreInvalidEscapes) throws ParserException {
|
||||
public AnsiShortcodeConverter(@NotNull String string, boolean ignoreInvalidEscapes) throws ParserException {
|
||||
super(string, ignoreInvalidEscapes);
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ public final class AnsiSubsystem extends SubsystemClass {
|
|||
if (instance == null)
|
||||
instance = this;
|
||||
else
|
||||
Logger.crash("Only one instance of this class is allowed, use getInstance() instead of creating a new instance");
|
||||
logger.crash("Only one instance of this class is allowed, use getInstance() instead of creating a new instance");
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
|
|
|
@ -27,17 +27,13 @@ import de.staropensource.engine.base.implementable.ShutdownHandler;
|
|||
import de.staropensource.engine.base.implementable.SubsystemClass;
|
||||
import de.staropensource.engine.base.implementable.helper.EventHelper;
|
||||
import de.staropensource.engine.base.implementation.versioning.StarOpenSourceVersioningSystem;
|
||||
import de.staropensource.engine.base.event.InternalEngineShutdownEvent;
|
||||
import de.staropensource.engine.base.internal.event.InternalEngineShutdownEvent;
|
||||
import de.staropensource.engine.base.internal.type.DependencySubsystemVector;
|
||||
import de.staropensource.engine.base.logging.PrintStreamService;
|
||||
import de.staropensource.engine.base.logging.*;
|
||||
import de.staropensource.engine.base.logging.backend.async.LoggingQueue;
|
||||
import de.staropensource.engine.base.logging.backend.async.LoggingThread;
|
||||
import de.staropensource.engine.base.type.DependencyVector;
|
||||
import de.staropensource.engine.base.type.EngineState;
|
||||
import de.staropensource.engine.base.type.immutable.ImmutableLinkedList;
|
||||
import de.staropensource.engine.base.utility.DependencyResolver;
|
||||
import de.staropensource.engine.base.utility.FileAccess;
|
||||
import de.staropensource.engine.base.utility.Miscellaneous;
|
||||
import de.staropensource.engine.base.utility.PlaceholderEngine;
|
||||
import de.staropensource.engine.base.utility.information.EngineInformation;
|
||||
|
@ -88,6 +84,14 @@ public final class Engine extends SubsystemClass {
|
|||
@Getter
|
||||
private static final ThreadGroup threadGroup = new ThreadGroup("sos!engine");
|
||||
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private static final LoggerInstance logger = new LoggerInstance.Builder().setClazz(Engine.class).setOrigin("ENGINE").setMetadata(EngineInformation.getVersioningCodename()).build();
|
||||
|
||||
/**
|
||||
* Contains the engine state.
|
||||
*
|
||||
|
@ -99,7 +103,7 @@ public final class Engine extends SubsystemClass {
|
|||
* @since v1-alpha2
|
||||
*/
|
||||
@Getter
|
||||
private @NotNull EngineState state;
|
||||
private @NotNull EngineState state = EngineState.UNKNOWN;
|
||||
|
||||
/**
|
||||
* Contains a list of all registered subsystems.
|
||||
|
@ -165,116 +169,57 @@ public final class Engine extends SubsystemClass {
|
|||
}
|
||||
|
||||
// Print warning about shutdown
|
||||
Logger.warn("Trying to shut down engine using shutdown hook.\nThis approach to shutting down the engine and JVM is NOT RECOMMENDED, please use Engine#shutdown() instead.");
|
||||
logger.warn("Trying to shut down engine using shutdown hook.\nThis approach to shutting down the engine and JVM is NOT RECOMMENDED, please use Engine#shutdown() instead.");
|
||||
|
||||
// Shutdown
|
||||
Engine.getInstance().shutdown();
|
||||
|
||||
// Print last message
|
||||
Logger.warn("Engine successfully shut down using shutdown hook. PLEASE USE Engine#shutdown() INSTEAD OF System#exit() or Runtime#exit()!");
|
||||
InitLogger.warn(getClass(), "ENGINE", EngineInformation.getVersioningCodename(), "Engine successfully shut down using shutdown hook. PLEASE USE Engine#shutdown() INSTEAD OF System#exit() or Runtime#exit()!");
|
||||
});
|
||||
|
||||
/**
|
||||
* Initializes the StarOpenSource Engine.
|
||||
*
|
||||
* @throws RuntimeException for all exceptions thrown by this constructor
|
||||
* @since v1-alpha8
|
||||
* @throws IllegalStateException when running in an incompatible environment
|
||||
* @since v1-alpha6
|
||||
*/
|
||||
private Engine() throws RuntimeException {
|
||||
try {
|
||||
private Engine() throws IllegalStateException {
|
||||
long initTime = Miscellaneous.measureExecutionTime(() -> {
|
||||
instance = this;
|
||||
state = EngineState.EARLY_STARTUP;
|
||||
|
||||
// For measuring the initialization time
|
||||
long initTimeEarly = System.currentTimeMillis();
|
||||
long initTimeLate = initTimeEarly;
|
||||
|
||||
// Check for incompatible JVM implementations
|
||||
checkJvmIncompatibilities();
|
||||
|
||||
// Display that the engine is initializing
|
||||
Logger.verb("Initializing engine");
|
||||
|
||||
// Start the logging thread
|
||||
Logger.diag("Starting logging infrastructure");
|
||||
LoggingThread.startThread(false);
|
||||
PrintStreamService.initializeStreams();
|
||||
|
||||
// Initialize EngineInternals
|
||||
Logger.diag("Initializing EngineInternals class");
|
||||
new EngineInternals();
|
||||
|
||||
// Load engine configuration
|
||||
Logger.diag("Loading engine configuration");
|
||||
new EngineConfiguration();
|
||||
EngineConfiguration.getInstance().loadConfiguration();
|
||||
|
||||
// Load engine build information
|
||||
Logger.diag("Loading engine build information");
|
||||
EngineInformation.update();
|
||||
logger.info("Initializing engine");
|
||||
initializeClasses(); // Initialize classes
|
||||
if (checkEnvironment()) // Check environment
|
||||
throw new IllegalStateException("Running in an incompatible environment");
|
||||
ensureEnvironment(); // Prepare the environment and ensure safety
|
||||
populateCrashContent(); // Populate crash content
|
||||
cacheEvents(); // Cache event listeners
|
||||
startThreads(); // Start threads
|
||||
|
||||
// Check for reflective classpath scanning compatibility
|
||||
checkReflectiveClasspathScanningCompatibility();
|
||||
|
||||
// Check for Java version incompatibilities
|
||||
checkJavaVersion();
|
||||
|
||||
// Initialize PlaceholderEngine
|
||||
Logger.diag("Initializing PlaceholderEngine");
|
||||
PlaceholderEngine.initialize();
|
||||
|
||||
// Initialize static FileAccess instances
|
||||
Logger.diag("Initializing static FileAccess instances");
|
||||
FileAccess.initializeInstances();
|
||||
|
||||
// Install the safety shutdown hook
|
||||
Logger.diag("Installing safety shutdown hook");
|
||||
EngineInternals.getInstance().installSafetyShutdownHook(true);
|
||||
|
||||
// Cache events
|
||||
Logger.diag("Caching event listeners");
|
||||
cacheEvents();
|
||||
|
||||
// Complete early initialization stage
|
||||
Logger.verb("Completing early initialization stage");
|
||||
logger.verb("Completing early initialization stage");
|
||||
state = EngineState.STARTUP;
|
||||
initTimeEarly = System.currentTimeMillis() - initTimeEarly;
|
||||
|
||||
// Perform automatic subsystem initialization
|
||||
if (EngineConfiguration.getInstance().isInitialPerformSubsystemInitialization()) {
|
||||
// Collect all subsystems
|
||||
Logger.diag("Collecting subsystems");
|
||||
collectSubsystems();
|
||||
collectSubsystems(); // Collect subsystems
|
||||
|
||||
// Initialize subsystems
|
||||
try {
|
||||
initializeSubsystems();
|
||||
} catch (Exception exception) {
|
||||
Logger.error("Subsystem dependency resolution failed");
|
||||
logger.error("Subsystem dependency resolution failed");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Complete late initialization stage
|
||||
Logger.verb("Completing late initialization stage");
|
||||
state = EngineState.RUNNING;
|
||||
initTimeLate = System.currentTimeMillis() - initTimeLate;
|
||||
|
||||
// Print welcome message
|
||||
Logger.info(
|
||||
"""
|
||||
Welcome to the StarOpenSource Engine "%engine_version_codename%" %engine_version%!
|
||||
Running commit %engine_git_commit_id_long% (dirty %engine_git_dirty%).
|
||||
Initialization took %init_time_total%ms (early %init_time_early%ms, late %init_time_late%ms).
|
||||
|
||||
Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
Licensed under the GNU Affero General Public License v3"""
|
||||
.replace("%init_time_total%", String.valueOf(initTimeEarly + initTimeLate))
|
||||
.replace("%init_time_early%", String.valueOf(initTimeEarly))
|
||||
.replace("%init_time_late%", String.valueOf(initTimeLate))
|
||||
);
|
||||
} catch (Exception exception) {
|
||||
throw new RuntimeException(exception);
|
||||
}
|
||||
logger.verb("Completing late initialization stage");
|
||||
state = EngineState.RUNNING;
|
||||
logger.info("Initialized sos!engine %engine_version% (commit %engine_git_commit_id_long%-%engine_git_branch%, dirty %engine_git_dirty%) in " + initTime + "ms\nThe StarOpenSource Engine is licensed under the GNU AGPL v3. Copyright (c) 2024 The StarOpenSource Engine Authors.");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -285,74 +230,147 @@ public final class Engine extends SubsystemClass {
|
|||
* @throws RuntimeException on engine initialization failure
|
||||
* @since v1-alpha6
|
||||
*/
|
||||
public static void initialize() throws RuntimeException {
|
||||
public static void initialize() throws IllegalStateException, RuntimeException {
|
||||
try {
|
||||
if (instance == null)
|
||||
new Engine();
|
||||
} catch (RuntimeException exception) {
|
||||
Logger.error("Engine initialization failed");
|
||||
Logger.error(Miscellaneous.getStackTraceHeader(exception.getCause()));
|
||||
for (String line : Miscellaneous.getStackTraceAsString(exception.getCause(), true).split("\n"))
|
||||
Logger.error(line);
|
||||
|
||||
throw new RuntimeException("Engine initialization failed", exception.getCause());
|
||||
} catch (IllegalStateException exception) {
|
||||
throw exception;
|
||||
} catch (Exception exception) {
|
||||
logger.error("Engine initialization failed");
|
||||
logger.error(Miscellaneous.getStackTraceHeader(exception));
|
||||
for (String line : Miscellaneous.getStackTraceAsString(exception, true).split("\n"))
|
||||
logger.error(line);
|
||||
throw new RuntimeException("Engine initialization failed", exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the running JVM implementation is not supported by the engine.
|
||||
* Initializes all classes.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private void checkJvmIncompatibilities() {
|
||||
if (System.getProperties().getProperty("sosengine.base.allowUnsupportedJVMInitialization", "false").equals("true")) {
|
||||
Logger.warn("Skipping JVM implementation incompatibilities check");
|
||||
return;
|
||||
}
|
||||
private void initializeClasses() {
|
||||
logger.verb("Initializing engine classes");
|
||||
new EngineInternals();
|
||||
PlaceholderEngine.initialize();
|
||||
|
||||
// Substrate VM (GraalVM Community)
|
||||
EngineInformation.update();
|
||||
PrintStreamService.initializeStreams();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the environment is compatible with the engine build.
|
||||
*
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
private boolean checkEnvironment() {
|
||||
logger.diag("Checking environment");
|
||||
|
||||
// Warn about potential Java incompatibilities
|
||||
if (JvmInformation.getJavaVersion() > EngineInformation.getJavaSource())
|
||||
logger.warn("The StarOpenSource Engine is running on an untested Java version.\nThings may not work as expected or features which can improve performance, stability, compatibility or ease of use may be missing.\nIf you encounter issues, try running a JVM implementing Java " + EngineInformation.getJavaSource());
|
||||
|
||||
// Shutdown if running in an unsupported JVM
|
||||
if (JvmInformation.getImplementationName().equals("Substrate VM") && JvmInformation.getImplementationVendor().equals("GraalVM Community")) {
|
||||
Logger.error("##############################################################################################");
|
||||
Logger.error("## Running in Substrate VM, which is the name of the JVM used by GraalVM native-image. ##");
|
||||
Logger.error("## The StarOpenSource Engine does not support native-image as using reflection in a certain ##");
|
||||
Logger.error("## way seems to cause the Substrate JVM to crash. Workarounds have failed. ##");
|
||||
Logger.error("## This has already been noted in issue #3, which you can view here: ##");
|
||||
Logger.error("## https://git.staropensource.de/StarOpenSource/Engine/issues/3 ##");
|
||||
Logger.error("## ##");
|
||||
Logger.error("## While this is sad, we unfortunately can't do anything against it unless we introduce ##");
|
||||
Logger.error("## annoying and stupid changes into the engine, which we don't want to do. ##");
|
||||
Logger.error("## ##");
|
||||
Logger.error("## We're truly sorry for this inconvenience. The sos!engine will now terminate. ##");
|
||||
Logger.error("##############################################################################################");
|
||||
logger.error("##############################################################################################");
|
||||
logger.error("## Running in Substrate VM, which is the name of the JVM used by GraalVM native-image. ##");
|
||||
logger.error("## The StarOpenSource Engine does not support native-image as using reflection in a certain ##");
|
||||
logger.error("## way seems to cause the Substrate JVM to crash. Workarounds have failed. ##");
|
||||
logger.error("## This has already been noted in issue #3, which you can view here: ##");
|
||||
logger.error("## https://git.staropensource.de/StarOpenSource/Engine/issues/3 ##");
|
||||
logger.error("## ##");
|
||||
logger.error("## While this is sad, we unfortunately can't do anything against it unless we introduce ##");
|
||||
logger.error("## annoying and stupid changes into the engine, which we don't want to do. ##");
|
||||
logger.error("## ##");
|
||||
logger.error("## We're truly sorry for this inconvenience. The sos!engine will now terminate. ##");
|
||||
logger.error("##############################################################################################");
|
||||
Runtime.getRuntime().exit(255);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if reflective classpath scanning is supported.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private void checkReflectiveClasspathScanningCompatibility() {
|
||||
// Check if reflective classpath scanning is supported
|
||||
if (System.getProperties().getProperty("sosengine.base.considerEnvironmentUnfriendlyToClasspathScanning", "false").equals("true")) {
|
||||
Logger.warn("Running in an classpath scanning-unfriendly environment, disabling classpath scanning support.");
|
||||
Logger.warn("If shit doesn't work and is expected to be discovered by annotations, you'll need to");
|
||||
Logger.warn("either register it first or have to update some engine configuration setting.");
|
||||
Logger.warn("Please consult sos!engine's documentation for more information about this issue.");
|
||||
if (checkClasspathScanningSupport()) {
|
||||
logger.warn("Running in an classpath scanning-unfriendly environment, disabling.");
|
||||
logger.warn("If shit doesn't work and is expected to be discovered by annotations, you'll need to");
|
||||
logger.warn("either register it first or have to place classes in some package.");
|
||||
logger.warn("Please consult sos!engine's documentation for more information about this issue.");
|
||||
EngineInternals.getInstance().overrideReflectiveClasspathScanning(false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks and warns if the Java version of the
|
||||
* running JVM is higher than the engine supports.
|
||||
* Returns whether scanning the classpath is supported.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @return test results
|
||||
* @since v1-alpha5
|
||||
*/
|
||||
private void checkJavaVersion() {
|
||||
if (JvmInformation.getJavaVersion() > EngineInformation.getJavaSource())
|
||||
Logger.warn("The StarOpenSource Engine is running on an untested Java version.\nThings may not work as expected or features which can improve performance, stability, compatibility or ease of use may be missing.\nIf you encounter issues, try running a JVM implementing Java " + EngineInformation.getJavaSource());
|
||||
private boolean checkClasspathScanningSupport() {
|
||||
// This may be expanded in the future
|
||||
return EngineConfiguration.getInstance().isInitialForceDisableClasspathScanning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the execution safety of the environment.
|
||||
*
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
private void ensureEnvironment() {
|
||||
EngineInternals.getInstance().installSafetyShutdownHook(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method populates {@link CrashHandler#crashContent} with content.
|
||||
*
|
||||
* @see CrashHandler#getCrashContent()
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "ExtractMethodRecommender" })
|
||||
private void populateCrashContent() {
|
||||
logger.diag("Populating crash content");
|
||||
|
||||
// Issuer
|
||||
Map<@NotNull String, @NotNull String> crashContentIssuer = new LinkedHashMap<>();
|
||||
crashContentIssuer.put("Code part", "%issuer_origin%");
|
||||
crashContentIssuer.put("Classpath", "%issuer_path%");
|
||||
crashContentIssuer.put("Additional information", "%issuer_metadata%");
|
||||
crashContentIssuer.put("Message", "%crash_message%");
|
||||
|
||||
// Engine -> Dependencies
|
||||
LinkedList<@NotNull String> crashContentEngineDependencies = new LinkedList<>();
|
||||
crashContentEngineDependencies.add("Subsystem 'base': Reflections: %engine_dependency_reflections%");
|
||||
crashContentEngineDependencies.add("Subsystem 'ansi': Jansi: %engine_dependency_jansi%");
|
||||
crashContentEngineDependencies.add("Subsystem 'slf4j-compat': SLF4J: %engine_dependency_slf4j%");
|
||||
crashContentEngineDependencies.add("Subsystems 'glfw', 'opengl' & 'vulkan': LWJGL: %engine_dependency_lwjgl%");
|
||||
// Engine -> *
|
||||
Map<@NotNull String, @NotNull Object> crashContentEngine = new LinkedHashMap<>();
|
||||
crashContentEngine.put("Version", "%engine_version%");
|
||||
crashContentEngine.put("Dependencies", crashContentEngineDependencies);
|
||||
|
||||
// JVM -> Implementation
|
||||
Map<@NotNull String, @NotNull String> crashContentJvmImplementation = new LinkedHashMap<>();
|
||||
crashContentJvmImplementation.put("Name", "%jvm_implementation_name%");
|
||||
crashContentJvmImplementation.put("Version", "%jvm_implementation_version%");
|
||||
crashContentJvmImplementation.put("Vendor", "%jvm_implementation_vendor%");
|
||||
// JVM -> *
|
||||
Map<@NotNull String, @NotNull Object> crashContentJvm = new LinkedHashMap<>();
|
||||
crashContentJvm.put("Java Version", "%jvm_java%");
|
||||
crashContentJvm.put("Implementation", crashContentJvmImplementation);
|
||||
crashContentJvm.put("Arguments", JvmInformation.getArguments());
|
||||
|
||||
// Operating system
|
||||
Map<@NotNull String, @NotNull String> crashContentOS = new LinkedHashMap<>();
|
||||
crashContentOS.put("Time", "%time_hour%:%time_minute%:%time_second% (%time_zone%, UNIX Epoch: %time_epoch%)");
|
||||
crashContentOS.put("Date", "%date_day%.%date_month%.%date_year%");
|
||||
|
||||
// Add to crash handler
|
||||
CrashHandler.getCrashContent().put("Issuer", crashContentIssuer);
|
||||
CrashHandler.getCrashContent().put("Engine", crashContentEngine);
|
||||
CrashHandler.getCrashContent().put("Java Virtual Machine", crashContentJvm);
|
||||
CrashHandler.getCrashContent().put("Operating system", crashContentOS);
|
||||
CrashHandler.getCrashContent().put("Stacktrace", "\n%stacktrace%");
|
||||
CrashHandler.getCrashContent().put("Stacktrace for all threads", "\n%stacktrace_all%");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -361,14 +379,30 @@ public final class Engine extends SubsystemClass {
|
|||
* @since v1-alpha0
|
||||
*/
|
||||
private void cacheEvents() {
|
||||
logger.diag("Caching events");
|
||||
|
||||
// Internal events
|
||||
EventHelper.cacheEvent(InternalEngineShutdownEvent.class);
|
||||
|
||||
// General events
|
||||
EventHelper.cacheEvent(EngineCrashEvent.class);
|
||||
EventHelper.cacheEvent(EngineShutdownEvent.class);
|
||||
EventHelper.cacheEvent(EngineSoftCrashEvent.class);
|
||||
EventHelper.cacheEvent(InternalEngineShutdownEvent.class);
|
||||
EventHelper.cacheEvent(LogEvent.class);
|
||||
EventHelper.cacheEvent(ThrowableCatchEvent.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts engine threads.
|
||||
*
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
public void startThreads() {
|
||||
logger.diag("Starting threads");
|
||||
|
||||
LoggingThread.startThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects all subsystems by their {@link EngineSubsystem} annotation.
|
||||
*
|
||||
|
@ -390,14 +424,14 @@ public final class Engine extends SubsystemClass {
|
|||
if (initializedClassRaw instanceof SubsystemClass)
|
||||
initializedClass = (SubsystemClass) initializedClassRaw;
|
||||
else
|
||||
Logger.crash("Failed to initialize subsystem " + clazz.getName() + ": Does not implement " + SubsystemClass.class.getName());
|
||||
logger.crash("Failed to initialize subsystem " + clazz.getName() + ": Does not implement " + SubsystemClass.class.getName());
|
||||
|
||||
//noinspection DataFlowIssue // the crash call will prevent a NullPointerException
|
||||
subsystemsMutable.add(new DependencySubsystemVector(initializedClass.getDependencyVector(), initializedClass));
|
||||
} catch (Exception exception) {
|
||||
if (exception.getClass() == IllegalStateException.class && exception.getMessage().startsWith("The version string is invalid: "))
|
||||
Logger.crash("Failed to initialize subsystem " + clazz.getName() + ": Invalid version string: " + exception.getMessage().replace("The version string is invalid: ", ""));
|
||||
Logger.crash("Failed to initialize subsystem " + clazz.getName() + ": Method invocation error", exception);
|
||||
logger.crash("Failed to initialize subsystem " + clazz.getName() + ": Invalid version string: " + exception.getMessage().replace("The version string is invalid: ", ""));
|
||||
logger.crash("Failed to initialize subsystem " + clazz.getName() + ": Method invocation error", exception);
|
||||
}
|
||||
|
||||
// Update 'subsystems'
|
||||
|
@ -427,10 +461,10 @@ public final class Engine extends SubsystemClass {
|
|||
} else
|
||||
for (String path : EngineConfiguration.getInstance().getInitialIncludeSubsystemClasses())
|
||||
try {
|
||||
Logger.diag("Resolving class " + path);
|
||||
logger.diag("Resolving class " + path);
|
||||
classes.add(Class.forName(path));
|
||||
} catch (ClassNotFoundException exception) {
|
||||
Logger.error("Failed loading subsystem class " + path + ": Class not found");
|
||||
logger.error("Failed loading subsystem class " + path + ": Class not found");
|
||||
}
|
||||
|
||||
return classes;
|
||||
|
@ -451,7 +485,7 @@ public final class Engine extends SubsystemClass {
|
|||
resolver.addVectors(subsystems);
|
||||
|
||||
// Resolve dependencies and get order
|
||||
Logger.diag("Resolving subsystem dependencies");
|
||||
logger.verb("Resolving subsystem dependencies");
|
||||
try {
|
||||
for (DependencyVector vector : resolver.resolve().getOrder()) // smol workaround
|
||||
order.add((DependencySubsystemVector) vector);
|
||||
|
@ -466,25 +500,25 @@ public final class Engine extends SubsystemClass {
|
|||
.append("- ")
|
||||
.append(error);
|
||||
|
||||
Logger.crash("Found unresolved dependencies:" + list, throwable);
|
||||
logger.crash("Found unresolved dependencies:" + list, throwable);
|
||||
return;
|
||||
}
|
||||
Logger.crash("An error occurred trying to resolve subsystem dependencies: " + throwable.getClass().getName() + (throwable.getMessage() == null ? "" : ": " + throwable.getMessage()));
|
||||
logger.crash("An error occurred trying to resolve subsystem dependencies: " + throwable.getClass().getName() + (throwable.getMessage() == null ? "" : ": " + throwable.getMessage()));
|
||||
throw throwable;
|
||||
}
|
||||
|
||||
// Initialize subsystems
|
||||
Logger.diag("Initializing engine subsystems");
|
||||
logger.verb("Initializing engine subsystems");
|
||||
long initTime;
|
||||
for (DependencySubsystemVector vector : subsystems) {
|
||||
Logger.diag("Initializing subsystem '" + vector.getSubsystemClass().getName() + "' (" + vector.getSubsystemClass().getClass().getName() + ")");
|
||||
logger.diag("Initializing subsystem " + vector.getSubsystemClass().getName() + " (" + vector.getSubsystemClass().getClass().getName() + ")");
|
||||
try {
|
||||
initTime = Miscellaneous.measureExecutionTime(() -> vector.getSubsystemClass().initializeSubsystem());
|
||||
} catch (Throwable throwable) {
|
||||
Logger.crash("An error occurred trying to initialize subsystem " + vector.getSubsystemClass().getName() + " (" + vector.getSubsystemClass().getClass().getName() + "): " + throwable.getClass().getName() + (throwable.getMessage() == null ? "" : ": " + throwable.getMessage()));
|
||||
logger.crash("An error occurred trying to initialize subsystem " + vector.getSubsystemClass().getName() + " (" + vector.getSubsystemClass().getClass().getName() + "): " + throwable.getClass().getName() + (throwable.getMessage() == null ? "" : ": " + throwable.getMessage()));
|
||||
throw throwable;
|
||||
}
|
||||
Logger.diag("Initialized subsystem '" + vector.getSubsystemClass().getName() + "' (" + vector.getSubsystemClass().getClass().getName() + ") in " + initTime + "ms");
|
||||
logger.diag("Initialized subsystem " + vector.getSubsystemClass().getName() + " (" + vector.getSubsystemClass().getClass().getName() + ") in " + initTime + "ms");
|
||||
}
|
||||
|
||||
// Update 'subsystems'
|
||||
|
@ -501,7 +535,7 @@ public final class Engine extends SubsystemClass {
|
|||
if (state == EngineState.UNKNOWN || state == EngineState.SHUTDOWN)
|
||||
return;
|
||||
|
||||
Logger.info("Shutting engine down");
|
||||
logger.info("Shutting engine down");
|
||||
if (state != EngineState.CRASHED)
|
||||
state = EngineState.SHUTDOWN;
|
||||
|
||||
|
@ -512,7 +546,7 @@ public final class Engine extends SubsystemClass {
|
|||
EngineConfiguration.getInstance().loadConfiguration(properties);
|
||||
|
||||
// Flush log messages
|
||||
LoggingQueue.flush();
|
||||
Logger.flushLogMessages();
|
||||
|
||||
// Disable safety shutdown hook
|
||||
try {
|
||||
|
@ -520,17 +554,14 @@ public final class Engine extends SubsystemClass {
|
|||
} catch (Exception ignored) {}
|
||||
|
||||
// Send events
|
||||
Logger.verb("Notifying classes about shutdown");
|
||||
logger.verb("Notifying classes about shutdown");
|
||||
new EngineShutdownEvent().callEvent();
|
||||
|
||||
Logger.verb("Notifying subsystems about shutdown");
|
||||
logger.verb("Notifying subsystems about shutdown");
|
||||
new InternalEngineShutdownEvent().callEvent();
|
||||
|
||||
// Delete scheduled files
|
||||
FileAccess.deleteScheduled();
|
||||
|
||||
// Invoke shutdown handler
|
||||
Logger.verb("Invoking shutdown handler (code " + exitCode + ")");
|
||||
logger.verb("Invoking shutdown handler (code " + exitCode + ")");
|
||||
shutdownHandler.shutdown((short) exitCode);
|
||||
}
|
||||
|
||||
|
@ -550,11 +581,7 @@ public final class Engine extends SubsystemClass {
|
|||
return "base";
|
||||
}
|
||||
|
||||
/**
|
||||
* This method does nothing.
|
||||
*
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void initializeSubsystem() {}
|
||||
|
||||
|
@ -607,12 +634,12 @@ public final class Engine extends SubsystemClass {
|
|||
Runtime.getRuntime().addShutdownHook(thread);
|
||||
Runtime.getRuntime().removeShutdownHook(thread);
|
||||
} catch (IllegalStateException exception) {
|
||||
Logger.warn("Terminating JVM: Already shutting down, skipping");
|
||||
logger.warn("Terminating JVM: Already shutting down, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
Logger.warn("Terminating JVM");
|
||||
Runtime.getRuntime().exit(exitCode);
|
||||
logger.warn("Terminating JVM");
|
||||
System.exit(exitCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,13 +19,11 @@
|
|||
|
||||
package de.staropensource.engine.base;
|
||||
|
||||
import de.staropensource.engine.base.annotation.EngineSubsystem;
|
||||
import de.staropensource.engine.base.event.LogEvent;
|
||||
import de.staropensource.engine.base.implementable.Configuration;
|
||||
import de.staropensource.engine.base.implementable.ShortcodeParser;
|
||||
import de.staropensource.engine.base.implementable.SubsystemClass;
|
||||
import de.staropensource.engine.base.logging.CrashHandler;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.logging.backend.async.LoggingThread;
|
||||
import de.staropensource.engine.base.logging.LoggingThread;
|
||||
import de.staropensource.engine.base.type.EngineState;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import de.staropensource.engine.base.type.vector.Vec2f;
|
||||
|
@ -35,7 +33,10 @@ import lombok.Getter;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides the base engine configuration.
|
||||
|
@ -69,11 +70,11 @@ public final class EngineConfiguration extends Configuration {
|
|||
private static EngineConfiguration instance;
|
||||
|
||||
/**
|
||||
* Contains the configuration prefix.
|
||||
* Contains prefix properties must begin with.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
* -- GETTER --
|
||||
* Returns the configuration prefix.
|
||||
* Returns prefix properties must begin with.
|
||||
*
|
||||
* @return property group
|
||||
* @since v1-alpha0
|
||||
|
@ -82,329 +83,261 @@ public final class EngineConfiguration extends Configuration {
|
|||
|
||||
|
||||
/**
|
||||
* Contains if debugging options should be allowed.
|
||||
* All debugging options will be forcefully set to
|
||||
* {@code false} if this option is set to {@code false}.
|
||||
* If enabled, allows for unintentional behaviour
|
||||
* and excess logging. Unless you want to debug or work
|
||||
* on a sensitive part of the engine, don't enable this!
|
||||
*
|
||||
* @since v1-alpha0
|
||||
* -- GETTER --
|
||||
* Returns if debugging options should be allowed.
|
||||
* All debugging options will be forcefully set to
|
||||
* {@code false} if this option is set to {@code false}.
|
||||
* Gets the value for {@link #debug}.
|
||||
*
|
||||
* @return debugging enabled?
|
||||
* @return variable value
|
||||
* @see #debug
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private boolean debug;
|
||||
|
||||
/**
|
||||
* Contains whether or not to log
|
||||
* events being emitted.
|
||||
* <p>
|
||||
* This will cause all events to
|
||||
* be logged, with the exception
|
||||
* of the {@link LogEvent}.
|
||||
* If enabled, all called events will be logged.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
* -- GETTER --
|
||||
* Returns whether or not to log
|
||||
* events being emitted.
|
||||
* <p>
|
||||
* This will cause all events to
|
||||
* be logged, with the exception
|
||||
* of the {@link LogEvent}.
|
||||
* Gets the value for {@link #debugEvents}.
|
||||
*
|
||||
* @return detailed event logging enabled?
|
||||
* @return variable value
|
||||
* @see #debugEvents
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private boolean debugEvents;
|
||||
|
||||
|
||||
/**
|
||||
* Contains whether or not to automatically discover
|
||||
* and initialize any class extending {@link SubsystemClass}
|
||||
* whilst being annotated with {@link EngineSubsystem}.
|
||||
* If enabled, will try to automatically initialize every
|
||||
* subsystem found though reflection.
|
||||
* <p>
|
||||
* This mechanism may fail in certain situations, where
|
||||
* manual subsystem initialization may be desired. Make
|
||||
* sure to disable this setting before engine startup
|
||||
* and then initialize all subsystems manually.
|
||||
* This however may fail in certain situation, where manual
|
||||
* subsystem initialization may be required. For this reason,
|
||||
* this can be turned off before the engine initializes. Please
|
||||
* note though that dependency resolution between subsystems
|
||||
* won't be performed, be careful when initializing subsystems manually.
|
||||
*
|
||||
* @see Engine
|
||||
* @since v1-alpha5
|
||||
* @since v1-alpha2
|
||||
* -- GETTER --
|
||||
* Returns whether or not to automatically discover
|
||||
* and initialize any class extending {@link SubsystemClass}
|
||||
* whilst being annotated with {@link EngineSubsystem}.
|
||||
* <p>
|
||||
* This mechanism may fail in certain situations, where
|
||||
* manual subsystem initialization may be desired. Make
|
||||
* sure to disable this setting before engine startup
|
||||
* and then initialize all subsystems manually.
|
||||
* Gets the value for {@link #initialPerformSubsystemInitialization}.
|
||||
*
|
||||
* @return automatically discover and initialize subsystems?
|
||||
* @return variable value
|
||||
* @see #initialPerformSubsystemInitialization
|
||||
* @since v1-alpha5
|
||||
*/
|
||||
private boolean initialPerformSubsystemInitialization;
|
||||
|
||||
/**
|
||||
* Contains a set of class names to try to load
|
||||
* and initialize as subsystems. Will only take effect
|
||||
* if {@link #initialPerformSubsystemInitialization} is
|
||||
* turned off.
|
||||
* If enabled, will force-disable reflective classpath scanning.
|
||||
*
|
||||
* @see EngineInternals#getReflectiveClasspathScanning()
|
||||
* @see EngineInternals#overrideReflectiveClasspathScanning(boolean)
|
||||
* @since v1-alpha5
|
||||
* -- GETTER --
|
||||
* Gets the value for {@link #initialForceDisableClasspathScanning}.
|
||||
*
|
||||
* @return variable value
|
||||
* @see #initialForceDisableClasspathScanning
|
||||
* @since v1-alpha5
|
||||
*/
|
||||
private boolean initialForceDisableClasspathScanning;
|
||||
|
||||
/**
|
||||
* Will try to load the specified classes as subsystems,
|
||||
* if reflective classpath scanning is disabled.
|
||||
*
|
||||
* @since v1-alpha5
|
||||
* -- GETTER --
|
||||
* Returns a set of class names to try to load
|
||||
* and initialize as subsystems. Will only take effect
|
||||
* if {@link #getInitialIncludeSubsystemClasses()} is
|
||||
* turned off.
|
||||
* Gets the value for {@link #initialIncludeSubsystemClasses}.
|
||||
*
|
||||
* @return set of class names to try and initialize as subsystems
|
||||
* @return variable value
|
||||
* @see #initialIncludeSubsystemClasses
|
||||
* @since v1-alpha5
|
||||
*/
|
||||
private Set<@NotNull String> initialIncludeSubsystemClasses;
|
||||
|
||||
|
||||
/**
|
||||
* Contains whether or not to complain about invalid
|
||||
* shortcodes.
|
||||
* <p>
|
||||
* Requires the active log level to be set at least
|
||||
* to {@link LogLevel#SILENT_WARNING} to have effect.
|
||||
* If enabled, will cause {@link ShortcodeParser} to print
|
||||
* invalid shortcodes as {@link LogLevel#SILENT_WARNING}s.
|
||||
*
|
||||
* @see #logLevel
|
||||
* @see ShortcodeParser
|
||||
* @see #loggerLevel
|
||||
* @since v1-alpha0
|
||||
* -- GETTER --
|
||||
* Returns whether or not to complain about invalid
|
||||
* shortcodes.
|
||||
* <p>
|
||||
* Requires the active log level to be set at least
|
||||
* to {@link LogLevel#SILENT_WARNING} to have effect.
|
||||
* Gets the value for {@link #errorShortcodeParser}.
|
||||
*
|
||||
* @return complain about invalid shortcodes?
|
||||
* @see #getLogLevel()
|
||||
* @return variable value
|
||||
* @see #errorShortcodeParser
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private boolean errorShortcodeParser;
|
||||
|
||||
|
||||
/**
|
||||
* Contains if to log asynchronously.
|
||||
* <p>
|
||||
* If enabled, will cause a logging thread
|
||||
* to spawn. All log messages will be queued
|
||||
* and printed after a set delay
|
||||
* ({@link #logPollingSpeed}).
|
||||
* Highly recommended to keep enabled, or
|
||||
* the performance of your application will
|
||||
* very likely suffer.
|
||||
* If enabled, will makes the {@link Logger} work asynchronous,
|
||||
* in a separate platform thread. Don't disable unless you want
|
||||
* your application to run <b>extremely</b> slowly.
|
||||
*
|
||||
* @see #logPollingSpeed
|
||||
* @see #loggerPollingSpeed
|
||||
* @see Thread
|
||||
* @since v1-alpha0
|
||||
* -- GETTER --
|
||||
* Returns if to log asynchronously.
|
||||
* <p>
|
||||
* If enabled, will cause a logging thread
|
||||
* to spawn. All log messages will be queued
|
||||
* and printed after a set delay
|
||||
* ({@link #getLogPollingSpeed()}).
|
||||
* Highly recommended to keep enabled, or
|
||||
* the performance of your application will
|
||||
* very likely suffer.
|
||||
* Gets the value for {@link #optimizeLogging}.
|
||||
*
|
||||
* @return log asynchronously?
|
||||
* @see #getLogPollingSpeed()
|
||||
* @return variable value
|
||||
* @see #optimizeLogging
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private boolean optimizeLogging;
|
||||
|
||||
/**
|
||||
* Contains whether or not to emit events
|
||||
* asynchronously.
|
||||
* <p>
|
||||
* This will cause a
|
||||
* <a href="https://openjdk.org/jeps/444">VirtualThread</a>
|
||||
* to spawn every time an event is emitted.
|
||||
* If enabled, will make all events asynchronous,
|
||||
* in separate virtual threads. Don't disable unless you
|
||||
* want your application to run slower.
|
||||
*
|
||||
* @see VirtualThread
|
||||
* @since v1-alpha0
|
||||
* -- GETTER --
|
||||
* Contains whether or not to emit events
|
||||
* asynchronously.
|
||||
* <p>
|
||||
* This will cause a
|
||||
* <a href="https://openjdk.org/jeps/444">VirtualThread</a>
|
||||
* to spawn every time an event is emitted.
|
||||
* Gets the value for {@link #optimizeEvents}.
|
||||
*
|
||||
* @return emit events asynchronously?
|
||||
* @return variable value
|
||||
* @see #optimizeEvents
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private boolean optimizeEvents;
|
||||
|
||||
|
||||
/**
|
||||
* Contains the minimum allowed log level.
|
||||
* <p>
|
||||
* The priority list is as follows (from high to low priority):
|
||||
* <ul>
|
||||
* <li>{@link LogLevel#CRASH}</li>
|
||||
* <li>{@link LogLevel#ERROR}</li>
|
||||
* <li>{@link LogLevel#WARNING}</li>
|
||||
* <li>{@link LogLevel#INFORMATIONAL}</li>
|
||||
* <li>{@link LogLevel#SILENT_WARNING}</li>
|
||||
* <li>{@link LogLevel#VERBOSE}</li>
|
||||
* <li>{@link LogLevel#DIAGNOSTIC}</li>
|
||||
* </ul>
|
||||
* Contains which logger levels are allowed
|
||||
* by setting the minimum logger level.
|
||||
*
|
||||
* @see Logger
|
||||
* @since v1-alpha0
|
||||
* -- GETTER --
|
||||
* Returns the minimum allowed log level.
|
||||
* <p>
|
||||
* The priority list is as follows (from high to low priority):
|
||||
* <ul>
|
||||
* <li>{@link LogLevel#CRASH}</li>
|
||||
* <li>{@link LogLevel#ERROR}</li>
|
||||
* <li>{@link LogLevel#WARNING}</li>
|
||||
* <li>{@link LogLevel#INFORMATIONAL}</li>
|
||||
* <li>{@link LogLevel#SILENT_WARNING}</li>
|
||||
* <li>{@link LogLevel#VERBOSE}</li>
|
||||
* <li>{@link LogLevel#DIAGNOSTIC}</li>
|
||||
* </ul>
|
||||
* Gets the value for {@link #loggerLevel}.
|
||||
*
|
||||
* @return minimum allowed log level
|
||||
* @return variable value
|
||||
* @see #loggerLevel
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private LogLevel logLevel;
|
||||
private LogLevel loggerLevel;
|
||||
|
||||
/**
|
||||
* Contains a comma-separated list of optional
|
||||
* features to add to the final log output.
|
||||
* <p>
|
||||
* Available features (in order of appearance):
|
||||
* <ul>
|
||||
* <li><code>formatting</code></li>
|
||||
* <li><code>runtime</code></li>
|
||||
* <li><code>date</code></li>
|
||||
* <li><code>time</code></li>
|
||||
* <li><code>shortIssuerClass</code></li>
|
||||
* <li><code>moduleName</code></li>
|
||||
* <li><code>moduleVersion</code> (requires <code>moduleName</code>)</li>
|
||||
* <li><code>methodName</code></li>
|
||||
* <li><code>lineNumber</code></li>
|
||||
* </ul>
|
||||
* Contains the logging template used for creating log messages.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @see Logger
|
||||
* @since v1-alpha0
|
||||
* -- GETTER --
|
||||
* Returns a comma-separated list of optional
|
||||
* features to add to the final log output.
|
||||
* <p>
|
||||
* Available features (in order of appearance):
|
||||
* <ul>
|
||||
* <li><code>formatting</code></li>
|
||||
* <li><code>runtime</code></li>
|
||||
* <li><code>date</code></li>
|
||||
* <li><code>time</code></li>
|
||||
* <li><code>shortIssuerClass</code></li>
|
||||
* <li><code>moduleName</code></li>
|
||||
* <li><code>moduleVersion</code> (requires <code>moduleName</code>)</li>
|
||||
* <li><code>methodName</code></li>
|
||||
* <li><code>lineNumber</code></li>
|
||||
* </ul>
|
||||
* Gets the value for {@link #loggerTemplate}
|
||||
*
|
||||
* @return optional features to enable
|
||||
* @since v1-alpha8
|
||||
* @return variable value
|
||||
* @see #loggerTemplate
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private Set<@NotNull String> logFeatures;
|
||||
private String loggerTemplate;
|
||||
|
||||
/**
|
||||
* Contains how fast the logging thread will
|
||||
* poll for queued messages in milliseconds.
|
||||
* This also causes messages to be buffered.
|
||||
* poll for queued messages. This also causes
|
||||
* messages to be buffered.
|
||||
* <p>
|
||||
* Only applies if {@code optimizeLogging} is turned on.
|
||||
* Values below {@code 1} will poll for queued messages
|
||||
* as fast as it can. This however has pretty much no
|
||||
* benefit. Leave it at {@code 5}, it works quite well.
|
||||
* Values below {@code 1} will poll for queued
|
||||
* messages as fast as it can. This however has pretty much
|
||||
* no benefit. Leave it at {@code 5}, it works quite well.
|
||||
*
|
||||
* @see #optimizeLogging
|
||||
* @since v1-alpha4
|
||||
* -- GETTER --
|
||||
* Contains how fast the logging thread will
|
||||
* poll for queued messages, in milliseconds.
|
||||
* This also causes messages to be buffered.
|
||||
* <p>
|
||||
* Only applies if {@code optimizeLogging} is turned on.
|
||||
* Values below {@code 1} will poll for queued messages
|
||||
* as fast as it can. This however has pretty much no
|
||||
* benefit. Leave it at {@code 5}, it works quite well.
|
||||
* Gets the value for {@link #loggerPollingSpeed}.
|
||||
*
|
||||
* @return logging thread polling speed in milliseconds
|
||||
* @see #isOptimizeLogging()
|
||||
* @return variable value
|
||||
* @see #loggerPollingSpeed
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
private int logPollingSpeed;
|
||||
private int loggerPollingSpeed;
|
||||
|
||||
/**
|
||||
* Contains whether or not to forcefully write
|
||||
* to the standard output instead of the
|
||||
* standard error stream.
|
||||
* <p>
|
||||
* This only applies to the {@link LogLevel#ERROR} and
|
||||
* {@link LogLevel#CRASH} log levels, as these use
|
||||
* the standard error stream by default.
|
||||
* If enabled, will force the {@link Logger} and {@link CrashHandler} to use
|
||||
* <a href="https://www.man7.org/linux/man-pages/man3/stderr.3.html">the standard output</a>
|
||||
* instead of <a href="https://www.man7.org/linux/man-pages/man3/stderr.3.html">the standard error</a>
|
||||
* for logging {@code ERROR} and {@code CRASH}.
|
||||
*
|
||||
* @see <a href="https://man7.org/linux/man-pages/man3/stderr.3.html">man page about standard streams</a>
|
||||
* @since v1-alpha0
|
||||
* -- GETTER --
|
||||
* Contains whether or not to forcefully write
|
||||
* to the standard output instead of the
|
||||
* standard error stream.
|
||||
* <p>
|
||||
* This only applies to the {@link LogLevel#ERROR} and
|
||||
* {@link LogLevel#CRASH} log levels, as these use
|
||||
* the standard error stream by default.
|
||||
* Gets the value for {@link #loggerForceStandardOutput}.
|
||||
*
|
||||
* @return force use stdout?
|
||||
* @see <a href="https://man7.org/linux/man-pages/man3/stderr.3.html">man page about standard streams</a>
|
||||
* @return variable value
|
||||
* @see #loggerForceStandardOutput
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private boolean logForceStandardOutput;
|
||||
private boolean loggerForceStandardOutput;
|
||||
|
||||
/**
|
||||
* If enabled, will enable support for printing
|
||||
* log messages on multiple lines. By enabling this
|
||||
* configuration setting, logger throughput will be
|
||||
* decreased slightly when encountering a log message
|
||||
* with newlines found in it. This performance hit is
|
||||
* negligible though and should not affect application
|
||||
* performance, especially with logger multi-threading
|
||||
* turned on (see {@link #optimizeLogging}).
|
||||
*
|
||||
* @since v1-alpha4
|
||||
* -- GETTER --
|
||||
* Gets the value for {@link #loggerEnableNewlineSupport}.
|
||||
*
|
||||
* @return variable value
|
||||
* @see #loggerEnableNewlineSupport
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
private boolean loggerEnableNewlineSupport;
|
||||
|
||||
/**
|
||||
* If enabled, the JVM will be shutdown immediately
|
||||
* after printing a fatal crash report. This will
|
||||
* prevent shutdown hooks from executing.
|
||||
* Note: This will also prevent Jansi and potentially other libraries
|
||||
* from removing temporary native libraries at shutdown.
|
||||
*
|
||||
* @see CrashHandler
|
||||
* @since v1-alpha0
|
||||
* -- GETTER --
|
||||
* Gets the value for {@link #loggerImmediateShutdown}.
|
||||
*
|
||||
* @return variable value
|
||||
* @see #loggerImmediateShutdown
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private boolean loggerImmediateShutdown;
|
||||
|
||||
|
||||
/**
|
||||
* Contains if to truncate the full path
|
||||
* of a class when invoking using their
|
||||
* {@link #toString()} method.
|
||||
* Will truncate the path of types when using
|
||||
* their {@code toString} method.
|
||||
* <p>
|
||||
* Here's an example: Lets say that you have a
|
||||
* {@link Vec2f} instance and want to convert
|
||||
* it to a String. You can do that by using
|
||||
* {@link Vec2f}'s {@link Vec2f#toString()}
|
||||
* method. With this flag disabled it will
|
||||
* return
|
||||
* {@code de.staropensource.engine.base.types.vectors.}{@link Vec2i}{@code (x=64 y=64)}.
|
||||
* With this flag enabled however the method will return
|
||||
* Here's an example: Lets say that you
|
||||
* have a {@link Vec2f} and to convert it
|
||||
* to a String, which you can do with
|
||||
* {@link Vec2f#toString()}. With this flag
|
||||
* disabled it would return
|
||||
* {@code de.staropensource.engine.base.types.vectors.}{@link Vec2i}{@code (x=64 y=64)},
|
||||
* with it however it would just return
|
||||
* {@link Vec2i}{@code (x=64 y=64)},
|
||||
* which is much smaller.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
* -- GETTER --
|
||||
* Returns if to truncate the full path
|
||||
* of a class when invoking using their
|
||||
* {@link #toString()} method.
|
||||
* <p>
|
||||
* Here's an example: Lets say that you have a
|
||||
* {@link Vec2f} instance and want to convert
|
||||
* it to a String. You can do that by using
|
||||
* {@link Vec2f}'s {@link Vec2f#toString()}
|
||||
* method. With this flag disabled it will
|
||||
* return
|
||||
* {@code de.staropensource.engine.base.types.vectors.}{@link Vec2i}{@code (x=64 y=64)}.
|
||||
* With this flag enabled however the method will return
|
||||
* {@link Vec2i}{@code (x=64 y=64)},
|
||||
* which is much smaller.
|
||||
* Gets the value for {@link #hideFullTypePath}.
|
||||
*
|
||||
* @return truncate class paths?
|
||||
* @return variable value
|
||||
* @see #hideFullTypePath
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private boolean hideFullTypePath;
|
||||
|
@ -416,7 +349,7 @@ public final class EngineConfiguration extends Configuration {
|
|||
* @since v1-alpha6
|
||||
*/
|
||||
EngineConfiguration() {
|
||||
super();
|
||||
super("ENGINE");
|
||||
|
||||
instance = this;
|
||||
|
||||
|
@ -433,6 +366,7 @@ public final class EngineConfiguration extends Configuration {
|
|||
case "debugEvents" -> debugEvents = parser.getBoolean(group + property);
|
||||
|
||||
case "initialPerformSubsystemInitialization" -> initialPerformSubsystemInitialization = parser.getBoolean(group + property);
|
||||
case "initialForceDisableClasspathScanning" -> initialForceDisableClasspathScanning = parser.getBoolean(group + property);
|
||||
case "initialIncludeSubsystemClasses" -> {
|
||||
initialIncludeSubsystemClasses = new HashSet<>();
|
||||
initialIncludeSubsystemClasses.addAll(Arrays.stream(parser.getString(group + property).split(",")).toList());
|
||||
|
@ -444,22 +378,23 @@ public final class EngineConfiguration extends Configuration {
|
|||
optimizeLogging = parser.getBoolean(group + property);
|
||||
|
||||
// Start logging thread automatically
|
||||
if (optimizeLogging && Engine.getInstance().getState() == EngineState.RUNNING) {
|
||||
LoggingThread.startThread(false);
|
||||
}
|
||||
if (optimizeLogging && Engine.getInstance().getState() == EngineState.RUNNING)
|
||||
LoggingThread.startThread();
|
||||
}
|
||||
case "optimizeEvents" -> optimizeEvents = parser.getBoolean(group + property);
|
||||
|
||||
case "logLevel" -> {
|
||||
case "loggerLevel" -> {
|
||||
try {
|
||||
logLevel = LogLevel.valueOf(parser.getString(group + property).toUpperCase());
|
||||
loggerLevel = LogLevel.valueOf(parser.getString(group + property).toUpperCase());
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
Logger.error("The log level '" + parser.getString(group + property) + "' is not valid");
|
||||
System.out.println("Logger level " + parser.getString(group + property) + " is not valid");
|
||||
}
|
||||
}
|
||||
case "logFeatures" -> logFeatures = Set.copyOf(Arrays.stream(parser.getString(group + property).split(",")).toList());
|
||||
case "logPollingSpeed" -> logPollingSpeed = parser.getInteger(group + property, true);
|
||||
case "logForceStandardOutput" -> logForceStandardOutput = parser.getBoolean(group + property);
|
||||
case "loggerTemplate" -> loggerTemplate = parser.getString(group + property);
|
||||
case "loggerPollingSpeed" -> loggerPollingSpeed = parser.getInteger(group + property, true);
|
||||
case "loggerForceStandardOutput" -> loggerForceStandardOutput = parser.getBoolean(group + property);
|
||||
case "loggerEnableNewlineSupport" -> loggerEnableNewlineSupport = parser.getBoolean(group + property);
|
||||
case "loggerImmediateShutdown" -> loggerImmediateShutdown = parser.getBoolean(group + property);
|
||||
|
||||
case "hideFullTypePath" -> hideFullTypePath = parser.getBoolean(group + property);
|
||||
}
|
||||
|
@ -482,6 +417,7 @@ public final class EngineConfiguration extends Configuration {
|
|||
debugEvents = false;
|
||||
|
||||
initialPerformSubsystemInitialization = true;
|
||||
initialForceDisableClasspathScanning = false;
|
||||
initialIncludeSubsystemClasses = new HashSet<>();
|
||||
|
||||
errorShortcodeParser = true;
|
||||
|
@ -489,10 +425,12 @@ public final class EngineConfiguration extends Configuration {
|
|||
optimizeLogging = true;
|
||||
optimizeEvents = true;
|
||||
|
||||
logLevel = LogLevel.INFORMATIONAL;
|
||||
logFeatures = Set.of("formatting", "time", "methodName", "lineNumber");
|
||||
logPollingSpeed = 5;
|
||||
logForceStandardOutput = false;
|
||||
loggerLevel = LogLevel.INFORMATIONAL;
|
||||
loggerTemplate = "%log_color_primary%[%time_hour%:%time_minute%:%time_second%] [%log_level% %log_path%%log_metadata%] %log_message_prefix%%log_color_primary%%log_color_secondary%%log_message%<reset>";
|
||||
loggerPollingSpeed = 5;
|
||||
loggerForceStandardOutput = false;
|
||||
loggerEnableNewlineSupport = true;
|
||||
loggerImmediateShutdown = false;
|
||||
|
||||
hideFullTypePath = false;
|
||||
}
|
||||
|
@ -505,6 +443,7 @@ public final class EngineConfiguration extends Configuration {
|
|||
case "debugEvents" -> debugEvents;
|
||||
|
||||
case "initialPerformSubsystemInitialization" -> initialPerformSubsystemInitialization;
|
||||
case "initialForceDisableClasspathScanning" -> initialForceDisableClasspathScanning;
|
||||
case "initialIncludeSubsystemClasses" -> initialIncludeSubsystemClasses;
|
||||
|
||||
case "errorShortcodeParser" -> errorShortcodeParser;
|
||||
|
@ -512,10 +451,12 @@ public final class EngineConfiguration extends Configuration {
|
|||
case "optimizeLogging" -> optimizeLogging;
|
||||
case "optimizeEvents" -> optimizeEvents;
|
||||
|
||||
case "logLevel" -> logLevel;
|
||||
case "logFeatures" -> logFeatures;
|
||||
case "logPollingSpeed" -> logPollingSpeed;
|
||||
case "logForceStandardOutput" -> logForceStandardOutput;
|
||||
case "loggerLevel" -> loggerLevel;
|
||||
case "loggerTemplate" -> loggerTemplate;
|
||||
case "loggerPollingSpeed" -> loggerPollingSpeed;
|
||||
case "loggerForceStandardOutput" -> loggerForceStandardOutput;
|
||||
case "loggerEnableNewlineSupport" -> loggerEnableNewlineSupport;
|
||||
case "loggerImmediateShutdown" -> loggerImmediateShutdown;
|
||||
|
||||
case "hideFullTypePath" -> hideFullTypePath;
|
||||
default -> null;
|
||||
|
|
|
@ -23,7 +23,7 @@ import de.staropensource.engine.base.exception.IllegalAccessException;
|
|||
import de.staropensource.engine.base.implementable.EventListenerCode;
|
||||
import de.staropensource.engine.base.implementable.ShutdownHandler;
|
||||
import de.staropensource.engine.base.implementable.helper.EventHelper;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import de.staropensource.engine.base.type.EventPriority;
|
||||
import de.staropensource.engine.base.type.InternalAccessArea;
|
||||
import lombok.Getter;
|
||||
|
@ -54,6 +54,14 @@ public final class EngineInternals {
|
|||
@Getter
|
||||
private static EngineInternals instance;
|
||||
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
private static final LoggerInstance logger = new LoggerInstance.Builder().setClazz(EngineInternals.class).setOrigin("ENGINE").build();
|
||||
|
||||
/**
|
||||
* Contains all disabled internal access areas.
|
||||
*
|
||||
|
@ -92,7 +100,7 @@ public final class EngineInternals {
|
|||
if (instance == null && Engine.getInstance() != null)
|
||||
instance = this;
|
||||
else
|
||||
Logger.crash("Only one instance of this class is allowed, use getInstance() instead of creating a new instance");
|
||||
logger.crash("Only one instance of this class is allowed, use getInstance() instead of creating a new instance");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package de.staropensource.engine.base.implementable;
|
||||
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import de.staropensource.engine.base.utility.PropertiesReader;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -36,11 +37,23 @@ import java.util.Properties;
|
|||
*/
|
||||
public abstract class Configuration {
|
||||
/**
|
||||
* Creates and initializes an instance of this abstract class.
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
protected final @NotNull LoggerInstance logger;
|
||||
|
||||
/**
|
||||
* Initializes this abstract class.
|
||||
*
|
||||
* @param origin see {@link LoggerInstance.Builder#setOrigin(String)}
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
protected Configuration() {
|
||||
protected Configuration(@NotNull String origin) {
|
||||
// Set logger instance
|
||||
logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin(origin).build();
|
||||
|
||||
// Load default configuration
|
||||
loadDefaultConfiguration();
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ public abstract class EventListenerCode {
|
|||
public @NotNull EventPriority priority = EventPriority.DEFAULT;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this abstract class.
|
||||
* Initializes this abstract class.
|
||||
*
|
||||
* @since v1-alpha5
|
||||
*/
|
||||
|
|
|
@ -22,6 +22,7 @@ package de.staropensource.engine.base.implementable;
|
|||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Interface for implementing custom logger implementations.
|
||||
|
@ -31,13 +32,43 @@ import org.jetbrains.annotations.NotNull;
|
|||
*/
|
||||
public interface LoggingAdapter {
|
||||
/**
|
||||
* Prints a log message.
|
||||
* Invoked before anything is done with the log message.
|
||||
*
|
||||
* @param level level of the log call
|
||||
* @param issuer {@link StackTraceElement} of the issuer
|
||||
* @param message raw message
|
||||
* @param format processed log call output (print this!)
|
||||
* @param level level
|
||||
* @param issuerClass issuer class
|
||||
* @param issuerOrigin issuer origin
|
||||
* @param issuerMetadata issuer metadata
|
||||
* @param message raw message
|
||||
* @param format unmodified log format
|
||||
* @return new log message, return {@code null} for no change
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
void print(@NotNull LogLevel level, @NotNull StackTraceElement issuer, @NotNull String message, @NotNull String format);
|
||||
@Nullable String prePlaceholder(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format);
|
||||
|
||||
/**
|
||||
* Invoked after placeholders have been processed and replaced.
|
||||
*
|
||||
* @param level level
|
||||
* @param issuerClass issuer class
|
||||
* @param issuerOrigin issuer origin
|
||||
* @param issuerMetadata issuer metadata
|
||||
* @param message raw message
|
||||
* @param format unmodified log format
|
||||
* @return new log format
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@NotNull String postPlaceholder(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format);
|
||||
|
||||
/**
|
||||
* Prints the log message.
|
||||
*
|
||||
* @param level level
|
||||
* @param issuerClass issuer class
|
||||
* @param issuerOrigin issuer origin
|
||||
* @param issuerMetadata issuer metadata
|
||||
* @param message raw message
|
||||
* @param format unmodified log format
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
void print(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ package de.staropensource.engine.base.implementable;
|
|||
|
||||
import de.staropensource.engine.base.EngineConfiguration;
|
||||
import de.staropensource.engine.base.exception.ParserException;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -29,7 +29,7 @@ import java.util.LinkedList;
|
|||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Base class for implementing a shortcode parser.
|
||||
* Base class for implementing a shortcode converter.
|
||||
* <p>
|
||||
* This class parses a string and converts it into a list of
|
||||
* components, which can then be in turn be converted to something
|
||||
|
@ -48,9 +48,16 @@ import java.util.Locale;
|
|||
*
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
@Getter
|
||||
@SuppressWarnings({ "JavadocDeclaration" })
|
||||
public abstract class ShortcodeParser {
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
protected final @NotNull LoggerInstance logger;
|
||||
|
||||
/**
|
||||
* Contains a list of components the parsed text is made out of.
|
||||
*
|
||||
|
@ -61,10 +68,11 @@ public abstract class ShortcodeParser {
|
|||
* @return component list
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
@Getter
|
||||
protected final @NotNull LinkedList<String> components;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this abstract class.
|
||||
* Initializes this abstract class.
|
||||
*
|
||||
* @param string string to parse
|
||||
* @param ignoreInvalidEscapes if {@code true}, will ignore and treat invalid escapes as text
|
||||
|
@ -72,6 +80,7 @@ public abstract class ShortcodeParser {
|
|||
* @since v1-alpha2
|
||||
*/
|
||||
protected ShortcodeParser(@NotNull String string, boolean ignoreInvalidEscapes) throws ParserException {
|
||||
logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("ENGINE").build();
|
||||
components = parse(string, ignoreInvalidEscapes);
|
||||
}
|
||||
|
||||
|
@ -125,7 +134,7 @@ public abstract class ShortcodeParser {
|
|||
else {
|
||||
// Complain about invalid shortcode
|
||||
if (EngineConfiguration.getInstance() != null && EngineConfiguration.getInstance().isErrorShortcodeParser())
|
||||
Logger.sarn("Invalid shortcode: " + part);
|
||||
logger.sarn("Invalid shortcode: " + part);
|
||||
|
||||
// Convert tag regular text
|
||||
components.add("TEXT:" + "<" + part + ">");
|
||||
|
@ -137,7 +146,7 @@ public abstract class ShortcodeParser {
|
|||
else {
|
||||
// Complain about invalid shortcode
|
||||
if (EngineConfiguration.getInstance() != null && EngineConfiguration.getInstance().isErrorShortcodeParser())
|
||||
Logger.sarn("Invalid shortcode: " + part);
|
||||
logger.sarn("Invalid shortcode: " + part);
|
||||
|
||||
// Convert tag regular text
|
||||
components.add("TEXT:" + "<" + part + ">");
|
||||
|
@ -165,7 +174,7 @@ public abstract class ShortcodeParser {
|
|||
else {
|
||||
// Complain about invalid shortcode
|
||||
if (EngineConfiguration.getInstance() != null && EngineConfiguration.getInstance().isErrorShortcodeParser())
|
||||
Logger.sarn("Invalid shortcode: " + part);
|
||||
logger.sarn("Invalid shortcode: " + part);
|
||||
|
||||
// Convert tag regular text
|
||||
components.add("TEXT:" + "<" + part + ">");
|
||||
|
|
|
@ -22,7 +22,8 @@ package de.staropensource.engine.base.implementable;
|
|||
import de.staropensource.engine.base.Engine;
|
||||
import de.staropensource.engine.base.annotation.EngineSubsystem;
|
||||
import de.staropensource.engine.base.annotation.EventListener;
|
||||
import de.staropensource.engine.base.event.InternalEngineShutdownEvent;
|
||||
import de.staropensource.engine.base.internal.event.InternalEngineShutdownEvent;
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import de.staropensource.engine.base.type.DependencyVector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -34,7 +35,15 @@ import org.jetbrains.annotations.NotNull;
|
|||
*/
|
||||
public abstract class SubsystemClass {
|
||||
/**
|
||||
* Creates and initializes an instance of this abstract class.
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public final LoggerInstance logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("ENGINE").build();
|
||||
|
||||
/**
|
||||
* Initializes this abstract class.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
|
|
|
@ -30,8 +30,7 @@ import de.staropensource.engine.base.exception.reflection.StaticInitializerExcep
|
|||
import de.staropensource.engine.base.implementable.Event;
|
||||
import de.staropensource.engine.base.implementable.EventListenerCode;
|
||||
import de.staropensource.engine.base.internal.implementation.EventListenerMethod;
|
||||
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import de.staropensource.engine.base.type.EventPriority;
|
||||
import de.staropensource.engine.base.utility.ListFormatter;
|
||||
import lombok.Getter;
|
||||
|
@ -53,6 +52,14 @@ import java.util.*;
|
|||
*/
|
||||
@Getter
|
||||
public final class EventHelper {
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
private static final @NotNull LoggerInstance logger = new LoggerInstance.Builder().setClazz(EventHelper.class).setOrigin("ENGINE").build();
|
||||
|
||||
/**
|
||||
* Holds all cached events.
|
||||
*
|
||||
|
@ -98,7 +105,7 @@ public final class EventHelper {
|
|||
cachedEventListeners.put(event, list);
|
||||
}
|
||||
|
||||
Logger.diag("Registered event listener " + eventListener + " for event " + event + " with priority " + priority.name());
|
||||
logger.diag("Registered event listener " + eventListener + " for event " + event + " with priority " + priority.name());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -170,26 +177,26 @@ public final class EventHelper {
|
|||
public static void invokeAnnotatedMethods(@NotNull Class<? extends Event> event, Object... arguments) {
|
||||
if (event != LogEvent.class && EngineConfiguration.getInstance().isDebugEvents())
|
||||
if (arguments.length == 0)
|
||||
Logger.diag("Event " + event.getName() + " was emitted");
|
||||
logger.diag("Event " + event.getName() + " was emitted");
|
||||
else
|
||||
Logger.diag("Event " + event.getName() + " was emitted, passing arguments " + ListFormatter.formatArray(arguments));
|
||||
logger.diag("Event " + event.getName() + " was emitted, passing arguments " + ListFormatter.formatArray(arguments));
|
||||
|
||||
Runnable eventCode = () -> {
|
||||
for (EventListenerCode eventListener : getAnnotatedMethods(event)) {
|
||||
try {
|
||||
eventListener.run(arguments);
|
||||
} catch (NoAccessException exception) {
|
||||
Logger.warn("Event listener " + eventListener + " could not be called as the method could not be accessed");
|
||||
logger.warn("Event listener " + eventListener + " could not be called as the method could not be accessed");
|
||||
} catch (InvalidMethodSignatureException exception) {
|
||||
Logger.warn("Event listener " + eventListener + " has an invalid method signature");
|
||||
logger.warn("Event listener " + eventListener + " has an invalid method signature");
|
||||
} catch (InvocationTargetException exception) {
|
||||
Logger.crash("Event listener " + eventListener + " threw a throwable", exception.getTargetException(), true);
|
||||
logger.crash("Event listener " + eventListener + " threw a throwable", exception.getTargetException(), true);
|
||||
} catch (InstanceMethodFromStaticContextException exception) {
|
||||
Logger.warn("Event listener " + eventListener + " is not static. Event listener methods must be static for them to be called.");
|
||||
logger.warn("Event listener " + eventListener + " is not static. Event listener methods must be static for them to be called.");
|
||||
} catch (StaticInitializerException exception) {
|
||||
Logger.crash("Event listener " + eventListener + " could not be called as the static initializer failed", exception.getThrowable(), true);
|
||||
logger.crash("Event listener " + eventListener + " could not be called as the static initializer failed", exception.getThrowable(), true);
|
||||
} catch (Exception exception) {
|
||||
Logger.crash("Event listener " + eventListener + " could not be called as an error occurred during reflection", exception, true);
|
||||
logger.crash("Event listener " + eventListener + " could not be called as an error occurred during reflection", exception, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -23,8 +23,9 @@ import de.staropensource.engine.base.EngineConfiguration;
|
|||
import de.staropensource.engine.base.implementable.LoggingAdapter;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import de.staropensource.engine.base.implementation.shortcode.EmptyShortcodeParser;
|
||||
import de.staropensource.engine.base.implementation.shortcode.EmptyShortcodeConverter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Prints log messages to the console, without any fancy colors or formatting.
|
||||
|
@ -43,10 +44,22 @@ public class PlainLoggingAdapter implements LoggingAdapter {
|
|||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void print(@NotNull LogLevel level, @NotNull StackTraceElement issuer, @NotNull String message, @NotNull String format) {
|
||||
format = new EmptyShortcodeParser(format, true).getClean();
|
||||
public @Nullable String prePlaceholder(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format) {
|
||||
return null; // No modifications necessary
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String postPlaceholder(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format) {
|
||||
return format; // No modifications necessary
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void print(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format) {
|
||||
format = new EmptyShortcodeConverter(format, true).getClean();
|
||||
if (level == LogLevel.ERROR || level == LogLevel.CRASH)
|
||||
if (EngineConfiguration.getInstance() != null && EngineConfiguration.getInstance().isLogForceStandardOutput())
|
||||
if (EngineConfiguration.getInstance() != null && EngineConfiguration.getInstance().isLoggerForceStandardOutput())
|
||||
System.out.println(format);
|
||||
else
|
||||
System.err.println(format);
|
||||
|
|
|
@ -40,5 +40,17 @@ public class QuietLoggingAdapter implements LoggingAdapter {
|
|||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void print(@NotNull LogLevel level, @NotNull StackTraceElement issuer, @NotNull String message, @NotNull String format) {}
|
||||
public @Nullable String prePlaceholder(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String postPlaceholder(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format) {
|
||||
return format;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void print(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format) {}
|
||||
}
|
||||
|
|
|
@ -44,9 +44,21 @@ public class RawLoggingAdapter implements LoggingAdapter {
|
|||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void print(@NotNull LogLevel level, @NotNull StackTraceElement issuer, @NotNull String message, @NotNull String format) {
|
||||
public @Nullable String prePlaceholder(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format) {
|
||||
return null; // No modifications necessary
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String postPlaceholder(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format) {
|
||||
return format; // No modifications necessary
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void print(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull String format) {
|
||||
if (level == LogLevel.ERROR || level == LogLevel.CRASH)
|
||||
if (EngineConfiguration.getInstance().isLogForceStandardOutput())
|
||||
if (EngineConfiguration.getInstance().isLoggerForceStandardOutput())
|
||||
System.out.println(format);
|
||||
else
|
||||
System.err.println(format);
|
||||
|
|
|
@ -27,18 +27,18 @@ import org.jetbrains.annotations.NotNull;
|
|||
* Cleans the string of any tags.
|
||||
*
|
||||
* @see ShortcodeParser
|
||||
* @since v1-alpha8
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
public final class EmptyShortcodeParser extends ShortcodeParser {
|
||||
public final class EmptyShortcodeConverter extends ShortcodeParser {
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @param string string to parse
|
||||
* @param ignoreInvalidEscapes if {@code true}, will ignore and treat invalid escapes as text
|
||||
* @throws ParserException on error
|
||||
* @since v1-alpha8
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
public EmptyShortcodeParser(@NotNull String string, boolean ignoreInvalidEscapes) throws ParserException {
|
||||
public EmptyShortcodeConverter(@NotNull String string, boolean ignoreInvalidEscapes) throws ParserException {
|
||||
super(string, ignoreInvalidEscapes);
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ public final class EmptyShortcodeParser extends ShortcodeParser {
|
|||
* Returns the parsed string without any tags.
|
||||
*
|
||||
* @return cleaned input string
|
||||
* @since v1-alpha8
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
public @NotNull String getClean() {
|
||||
StringBuilder output = new StringBuilder();
|
|
@ -17,7 +17,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.event;
|
||||
package de.staropensource.engine.base.internal.event;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Event;
|
||||
import de.staropensource.engine.base.implementable.helper.EventHelper;
|
|
@ -18,8 +18,7 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* Implementations for various interfaces and abstract classes.
|
||||
*
|
||||
* @since v1-alpha6
|
||||
* Events used for engine-internal communication.
|
||||
* These events are meant to be listened on by the base engine and it's subsystems.
|
||||
*/
|
||||
package de.staropensource.engine.base.implementation;
|
||||
package de.staropensource.engine.base.internal.event;
|
|
@ -17,30 +17,40 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder;
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.crashhandler;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import de.staropensource.engine.base.utility.information.EngineInformation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implements the {@code engine_version_codename} placeholder.
|
||||
* Implements the {@code crash_message} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha8
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class EngineVersionCodename implements Placeholder {
|
||||
public final class CrashMessage implements Placeholder {
|
||||
/**
|
||||
* Contains the message to use.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@NotNull
|
||||
private final String message;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @param message message to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public EngineVersionCodename() {}
|
||||
public CrashMessage(@NotNull String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
return text.replace("%engine_version_codename%", EngineInformation.getVersioningCodename());
|
||||
return text.replace("%crash_message%", message);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.crashhandler;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implements the {@code issuer_class} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class IssuerClass implements Placeholder {
|
||||
/**
|
||||
* Contains the issuer class to use.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private final @NotNull Class<?> issuerClass;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @param issuerClass issuer class to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public IssuerClass(@NotNull Class<?> issuerClass) {
|
||||
this.issuerClass = issuerClass;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
return text.replace("%issuer_class%", issuerClass.getName().replace(issuerClass.getPackageName() + ".", ""));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.crashhandler;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Implements the {@code issuer_metadata} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class IssuerMetadata implements Placeholder {
|
||||
/**
|
||||
* Contains the issuer metadata to use.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private final @Nullable String issuerMetadata;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @param issuerMetadata issuer metadata to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public IssuerMetadata(@Nullable String issuerMetadata) {
|
||||
this.issuerMetadata = issuerMetadata;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
String replacement = "\\<none>";
|
||||
|
||||
if (issuerMetadata != null)
|
||||
replacement = issuerMetadata;
|
||||
|
||||
return text.replace("%issuer_metadata%", replacement);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.crashhandler;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Implements the {@code issuer_origin} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class IssuerOrigin implements Placeholder {
|
||||
/**
|
||||
* Contains the issuer origin to use.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private final @NotNull String issuerOrigin;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @param issuerOrigin issuer origin to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public IssuerOrigin(@NotNull String issuerOrigin) {
|
||||
this.issuerOrigin = issuerOrigin;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
return text.replace("%issuer_origin%", issuerOrigin.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.crashhandler;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implements the {@code issuer_package} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class IssuerPackage implements Placeholder {
|
||||
/**
|
||||
* Contains the issuer class to use.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private final @NotNull Class<?> issuerClass;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @param issuerClass issuer class to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public IssuerPackage(@NotNull Class<?> issuerClass) {
|
||||
this.issuerClass = issuerClass;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
return text.replace("%issuer_package%", issuerClass.getPackageName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.crashhandler;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implements the {@code issuer_path} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class IssuerPath implements Placeholder {
|
||||
/**
|
||||
* Contains the issuer class to use.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private final @NotNull Class<?> issuerClass;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @param issuerClass issuer class to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public IssuerPath(@NotNull Class<?> issuerClass) {
|
||||
this.issuerClass = issuerClass;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
return text.replace("%issuer_path%", issuerClass.getName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.crashhandler;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import de.staropensource.engine.base.utility.Miscellaneous;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
/**
|
||||
* Implements the {@code stacktrace} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class Stacktrace implements Placeholder {
|
||||
/**
|
||||
* Contains the {@link Throwable} to use.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private final @Nullable Throwable throwable;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @param throwable {@link Throwable} to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public Stacktrace(@Nullable Throwable throwable) {
|
||||
this.throwable = throwable;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
return text.replace("%stacktrace%", throwable == null ? "No stack trace is available." : getFullStackTrace(throwable));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full stack trace.
|
||||
*
|
||||
* @param throwable throwable get the full stack trace of
|
||||
* @return full stack trace
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@NotNull
|
||||
private static String getFullStackTrace(@NotNull Throwable throwable) {
|
||||
return getFullStackTrace(throwable, new StringBuilder());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the full stack trace.
|
||||
*
|
||||
* @param throwable throwable to operate on
|
||||
* @param stacktrace stacktrace to append and return
|
||||
* @return full stack trace
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@NotNull
|
||||
private static String getFullStackTrace(@NotNull Throwable throwable, @NotNull StringBuilder stacktrace) {
|
||||
// Add newline
|
||||
if (!stacktrace.isEmpty())
|
||||
stacktrace.append("\n");
|
||||
|
||||
// Append stack trace
|
||||
stacktrace
|
||||
.append(Miscellaneous.getStackTraceHeader(throwable))
|
||||
.append("\n")
|
||||
.append(Miscellaneous.getStackTraceAsString(throwable, true));
|
||||
|
||||
// Handle throwables which contain other throwables
|
||||
if (throwable instanceof InvocationTargetException invocationTargetException)
|
||||
getFullStackTrace(invocationTargetException.getTargetException(), stacktrace);
|
||||
|
||||
// Return stack trace
|
||||
return stacktrace
|
||||
.toString()
|
||||
.replace("<", "\\<");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.crashhandler;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import de.staropensource.engine.base.utility.Miscellaneous;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Implements the {@code stacktrace_all} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class StacktraceAll implements Placeholder {
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public StacktraceAll() {}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
Map<Thread, StackTraceElement[]> stacktraces = Thread.getAllStackTraces();
|
||||
|
||||
for (Thread thread : stacktraces.keySet()) {
|
||||
if (!output.isEmpty())
|
||||
output.append("\n\n");
|
||||
output
|
||||
.append(thread.getName())
|
||||
.append(" (id=")
|
||||
.append(thread.threadId())
|
||||
.append(" priority=")
|
||||
.append(thread.getPriority())
|
||||
.append(" group=")
|
||||
.append(thread.getThreadGroup() == null ? "<unknown>" : thread.getThreadGroup().getName())
|
||||
.append(" state=")
|
||||
.append(thread.getState().name())
|
||||
.append(" daemon=")
|
||||
.append(thread.isDaemon())
|
||||
.append(")")
|
||||
.append("\n")
|
||||
.append(Miscellaneous.getStackTraceAsString(stacktraces.get(thread), false));
|
||||
}
|
||||
|
||||
return text.replace("%stacktrace_all%", output.toString().replace("<", "\\<"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Placeholders used in the {@link de.staropensource.engine.base.logging.CrashHandler}.
|
||||
*
|
||||
* @see de.staropensource.engine.base.logging.CrashHandler
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.crashhandler;
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.logger;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implements the {@code log_class} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class LogClass implements Placeholder {
|
||||
/**
|
||||
* Contains the issuer class to use.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private final @NotNull Class<?> issuerClass;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @param issuerClass issuer class to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public LogClass(@NotNull Class<?> issuerClass) {
|
||||
this.issuerClass = issuerClass;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
return text.replace("%log_class%", issuerClass.getName().replace(issuerClass.getPackageName() + ".", ""));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.logger;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implements the {@code log_color_primary} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class LogColorPrimary implements Placeholder {
|
||||
/**
|
||||
* Contains the {@link LogLevel} to use.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@NotNull
|
||||
private final LogLevel level;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @param level {@link LogLevel} to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public LogColorPrimary(@NotNull LogLevel level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
String color;
|
||||
|
||||
switch (level) {
|
||||
case DIAGNOSTIC, VERBOSE -> color = "<fg:blue>";
|
||||
case SILENT_WARNING, WARNING -> color = "<fg:yellow>";
|
||||
case INFORMATIONAL -> color = "<fg:white>";
|
||||
case ERROR -> color = "<fg:red>";
|
||||
case CRASH -> color = "You should not be seeing this";
|
||||
default -> color = "";
|
||||
}
|
||||
|
||||
return text.replace("%log_color_primary%", color);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.logger;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implements the {@code log_color_secondary} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class LogColorSecondary implements Placeholder {
|
||||
/**
|
||||
* Contains the {@link LogLevel} to use.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@NotNull
|
||||
private final LogLevel level;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @param level {@link LogLevel} to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public LogColorSecondary(@NotNull LogLevel level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
String color;
|
||||
|
||||
switch (level) {
|
||||
case DIAGNOSTIC, SILENT_WARNING -> color = "<italic>";
|
||||
default -> color = "";
|
||||
}
|
||||
|
||||
return text.replace("%log_color_secondary%", color);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.logger;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implements the {@code log_level} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class LogLevelEvent implements Placeholder {
|
||||
/**
|
||||
* Contains the {@link LogLevel} to use.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@NotNull
|
||||
private final LogLevel level;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @param level {@link LogLevel} to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public LogLevelEvent(@NotNull LogLevel level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
String levelText;
|
||||
|
||||
switch (level) {
|
||||
case CRASH -> levelText = "CRSH";
|
||||
case ERROR -> levelText = "ERR!";
|
||||
case SILENT_WARNING -> levelText = "SARN";
|
||||
default -> levelText = level.name().substring(0, 4);
|
||||
}
|
||||
|
||||
return text.replace("%log_level%", levelText);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.logger;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Implements the {@code log_metadata} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class LogMetadata implements Placeholder {
|
||||
/**
|
||||
* Contains the issuer metadata to use
|
||||
*
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private final @Nullable String issuerMetadata;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @param issuerMetadata issuer metadata to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public LogMetadata(@Nullable String issuerMetadata) {
|
||||
this.issuerMetadata = issuerMetadata;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
String replacement = "";
|
||||
|
||||
if (issuerMetadata != null && !issuerMetadata.isEmpty())
|
||||
replacement = "/" + issuerMetadata;
|
||||
|
||||
return text.replace("%log_metadata%", replacement);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.logger;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Implements the {@code log_origin} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class LogOrigin implements Placeholder {
|
||||
/**
|
||||
* Contains the issuer origin to use.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private final @NotNull String issuerOrigin;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @param issuerOrigin issuer origin to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public LogOrigin(@NotNull String issuerOrigin) {
|
||||
this.issuerOrigin = issuerOrigin;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
return text.replace("%log_origin%", issuerOrigin.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.logger;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implements the {@code log_package} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class LogPackage implements Placeholder {
|
||||
/**
|
||||
* Contains the issuer class to use.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private final @NotNull Class<?> issuerClass;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @param issuerClass issuer class to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public LogPackage(@NotNull Class<?> issuerClass) {
|
||||
this.issuerClass = issuerClass;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
return text.replace("%log_package%", issuerClass.getPackageName());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.logger;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Implements the {@code log_path} placeholder.
|
||||
*
|
||||
* @see Placeholder
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class LogPath implements Placeholder {
|
||||
/**
|
||||
* Contains the issuer class to use.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@NotNull
|
||||
private final Class<?> issuerClass;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this event.
|
||||
*
|
||||
* @param issuerClass issuer class to use
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public LogPath(@NotNull Class<?> issuerClass) {
|
||||
this.issuerClass = issuerClass;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String replace(@NotNull String text) {
|
||||
return text.replace("%log_path%", issuerClass.getName());
|
||||
}
|
||||
}
|
|
@ -18,9 +18,9 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* Everything related to making the logging
|
||||
* infrastructure asynchronous.
|
||||
* Placeholders used in the {@link de.staropensource.engine.base.logging.Logger}.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @see de.staropensource.engine.base.logging.Logger
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
package de.staropensource.engine.base.logging.backend.async;
|
||||
package de.staropensource.engine.base.internal.implementation.placeholder.logger;
|
|
@ -19,25 +19,33 @@
|
|||
|
||||
package de.staropensource.engine.base.internal.type;
|
||||
|
||||
import de.staropensource.engine.base.logging.backend.async.LoggingThread;
|
||||
import de.staropensource.engine.base.logging.LoggingThread;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a queued log call.
|
||||
* Represents a queued log message.
|
||||
*
|
||||
* @param level level of the log call
|
||||
* @param issuer {@link StackTraceElement} of the issuer
|
||||
* @param message message
|
||||
* @see LoggingThread
|
||||
* @since v1-alpha8
|
||||
* @param level Level of the log call.
|
||||
* @param issuerClass Class of the issuer.
|
||||
* @param issuerOrigin Origin of the issuer.
|
||||
* @param issuerMetadata Metadata about the issuer.
|
||||
* @param message Message of the log call.
|
||||
* @see LoggingThread#startThread()
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public record QueuedLogCall(@NotNull LogLevel level, @NotNull StackTraceElement issuer, @NotNull String message) {
|
||||
public record QueuedLogMessage(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @param level level
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
public QueuedLogCall {}
|
||||
public QueuedLogMessage {}
|
||||
}
|
|
@ -0,0 +1,280 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.logging;
|
||||
|
||||
import de.staropensource.engine.base.Engine;
|
||||
import de.staropensource.engine.base.EngineConfiguration;
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import de.staropensource.engine.base.event.EngineCrashEvent;
|
||||
import de.staropensource.engine.base.event.EngineSoftCrashEvent;
|
||||
import de.staropensource.engine.base.internal.implementation.placeholder.crashhandler.*;
|
||||
import de.staropensource.engine.base.type.EngineState;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import de.staropensource.engine.base.utility.PlaceholderEngine;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Handles engine and application crashes.
|
||||
*
|
||||
* @see Logger
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "JavadocDeclaration" })
|
||||
public final class CrashHandler {
|
||||
/**
|
||||
* Contains the template used to print a crash report.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
* -- GETTER --
|
||||
* Gets the template used to print a crash report.
|
||||
*
|
||||
* @return crash report template
|
||||
* @since v1-alpha0
|
||||
* -- SETTER --
|
||||
* Sets the template used to print a crash report.
|
||||
*
|
||||
* @param crashTemplate new crash template
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
private static @NotNull String crashTemplate = """
|
||||
<fg:red><bold>
|
||||
%handled%
|
||||
------------------------
|
||||
sos!engine crash
|
||||
------------------------
|
||||
|
||||
Dear user: The application or game you've tried to use seems to have made an oopsie. Please report this to the developer so they can fix it! Thank you \\<3
|
||||
Dear developer: FIX YOUR GODDAMN SHIT! Please check if your code or 3rd party subsystems are causing problems.
|
||||
If not, please report it at https://git.staropensource.de/StarOpenSource/Engine/issues. Thank you \\<3
|
||||
%content%
|
||||
|
||||
Dear user: The application or game you've tried to use seems to have made an oopsie. Please report this to the developer so they can fix it! Thank you \\<3
|
||||
Dear developer: FIX YOUR GODDAMN SHIT! Please check if your code or 3rd party subsystems are causing problems.
|
||||
If not, please report it at https://git.staropensource.de/StarOpenSource/Engine/issues. Thank you \\<3
|
||||
|
||||
------------------------
|
||||
sos!engine crash
|
||||
------------------------
|
||||
%handled%<reset>
|
||||
""";
|
||||
|
||||
/**
|
||||
* Contains nested {@link LinkedHashMap}s that contain the content printed at the time of a crash.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
* -- GETTER --
|
||||
* Returns the nested {@link LinkedHashMap}s that contain the content printed at the time of a crash.
|
||||
*
|
||||
* @return crash content
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@Getter
|
||||
private static final @NotNull LinkedHashMap<@NotNull Object, @NotNull Object> crashContent = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @since v1-alpha6
|
||||
*/
|
||||
private CrashHandler() {}
|
||||
|
||||
/**
|
||||
* Handles a crash.
|
||||
*
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message crash error detail
|
||||
* @param throwable simply to provide stacktrace and further insight into the crash, can be set to {@code null}
|
||||
* @param throwableHandled declares the throwable has handled, not causing the engine to shutdown
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public static synchronized void handleCrash(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @Nullable Throwable throwable, boolean throwableHandled) {
|
||||
Engine.getInstance().setState(EngineState.CRASHED); // Update engine state
|
||||
|
||||
// Prevent throwable handled warning if set to true but no throwable has been supplied
|
||||
if (throwable == null)
|
||||
throwableHandled = false;
|
||||
|
||||
// Escape message
|
||||
message = message
|
||||
.replace("\n", "\n ")
|
||||
.replace("\\", "\\\\")
|
||||
.replace("<", "\\<");
|
||||
|
||||
// Replace %content% and %handled%
|
||||
String base = crashTemplate
|
||||
.replace("%content%", processCrashContent())
|
||||
.replace("%handled%", throwableHandled ? "!!! This throwable is declared as handled and has been passed down the execution chain !!!" : "");
|
||||
|
||||
// Invoke LoggingAdapter#prePlaceholder
|
||||
String temp = Logger.getLoggingAdapter().prePlaceholder(LogLevel.CRASH, issuerClass, issuerOrigin, issuerMetadata, message, base);
|
||||
if (temp != null)
|
||||
base = temp;
|
||||
|
||||
// Create list of temporary placeholders
|
||||
List<@NotNull Placeholder> temporaryPlaceholders = new ArrayList<>();
|
||||
temporaryPlaceholders.add(new CrashMessage(message)); // log_message is out of order to allow for placeholder usage
|
||||
|
||||
// issuer_*
|
||||
temporaryPlaceholders.add(new IssuerClass(issuerClass));
|
||||
temporaryPlaceholders.add(new IssuerOrigin(issuerOrigin));
|
||||
temporaryPlaceholders.add(new IssuerMetadata(issuerMetadata));
|
||||
temporaryPlaceholders.add(new IssuerPackage(issuerClass));
|
||||
temporaryPlaceholders.add(new IssuerPath(issuerClass));
|
||||
// stacktrace*
|
||||
temporaryPlaceholders.add(new Stacktrace(throwable));
|
||||
temporaryPlaceholders.add(new StacktraceAll());
|
||||
|
||||
// Replace placeholders
|
||||
base = PlaceholderEngine.getInstance().process(base, temporaryPlaceholders);
|
||||
|
||||
// Invoke LoggingAdapter#postPlaceholder
|
||||
base = Logger.getLoggingAdapter().postPlaceholder(LogLevel.CRASH, issuerClass, issuerOrigin, issuerMetadata, message, base);
|
||||
|
||||
// Print log message by invoking LoggingAdapter#print
|
||||
Logger.flushLogMessages();
|
||||
Logger.getLoggingAdapter().print(LogLevel.CRASH, issuerClass, issuerOrigin, issuerMetadata, message, base);
|
||||
|
||||
// Emit event
|
||||
if (throwableHandled)
|
||||
new EngineSoftCrashEvent().callEvent();
|
||||
else
|
||||
new EngineCrashEvent().callEvent();
|
||||
|
||||
// Shutdown engine/JVM
|
||||
if (!throwableHandled)
|
||||
if (EngineConfiguration.getInstance().isLoggerImmediateShutdown()) {
|
||||
System.out.println("Halting JVM");
|
||||
Runtime.getRuntime().halt(69);
|
||||
} else
|
||||
Engine.getInstance().shutdown(69);
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method for generating the crash message content. Do not call.
|
||||
*
|
||||
* @param map {@link LinkedHashMap} to process
|
||||
* @param indentationSize indentation level
|
||||
* @return crash content string
|
||||
* @see #processCrashContent()
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
/*
|
||||
* Note: This entire method causes a compilation warning as we are using "unchecked or unsecure operations".
|
||||
* We can safely ignore this as this method
|
||||
* 1. checks data types before using them,
|
||||
* 2. only works on Strings, Lists and LinkedHashMaps and not on Files or something which could cause damage, and
|
||||
* 3. we can trust our own engine and possibly subsystems not doing shit in here.
|
||||
* As a subsystem developer you'll likely want useful crash information.
|
||||
*
|
||||
* But hey, if someone breaks this method (which may be possible idk didn't test it) then congrats!
|
||||
*/
|
||||
private static @NotNull String processCrashContent(@NotNull LinkedHashMap<Object, Object> map, int indentationSize) {
|
||||
StringBuilder content = new StringBuilder();
|
||||
|
||||
for (Object key : map.keySet()) {
|
||||
// Ensure key is of type String
|
||||
if (!(key instanceof String))
|
||||
continue;
|
||||
|
||||
// Ensure value is of type String, List or LinkedHashMap
|
||||
if (!(map.get(key) instanceof String
|
||||
|| map.get(key) instanceof List<?>
|
||||
|| map.get(key) instanceof LinkedHashMap<?, ?>))
|
||||
// Invalid content value, skip
|
||||
continue;
|
||||
|
||||
// Add newline
|
||||
if (!content.isEmpty())
|
||||
content.append("\n");
|
||||
|
||||
// Indent key
|
||||
if (indentationSize == 0)
|
||||
content.append("\n");
|
||||
else
|
||||
content
|
||||
.append(" ".repeat(indentationSize))
|
||||
.append("-> ");
|
||||
|
||||
if (map.get(key) == null)
|
||||
// Append key name, there's no content
|
||||
// Format: %key%
|
||||
content.append(key);
|
||||
else if (map.get(key) instanceof String)
|
||||
// Append key and it's value
|
||||
// Format: %key%: %value%
|
||||
content
|
||||
.append(key)
|
||||
.append(": ")
|
||||
.append(map.get(key));
|
||||
else if (map.get(key) instanceof List<?>) {
|
||||
// Append key and list the list's contents
|
||||
content.append(key);
|
||||
|
||||
for (Object item : (List<?>) map.get(key)) {
|
||||
if (item instanceof String)
|
||||
item = ((String) item)
|
||||
.replace("\\", "\\\\")
|
||||
.replace("\n", "\\n")
|
||||
.replace("\"", "\\\"");
|
||||
|
||||
content
|
||||
.append("\n")
|
||||
.append(" ".repeat(indentationSize))
|
||||
.append(" -> ")
|
||||
.append(item);
|
||||
}
|
||||
} else
|
||||
// So this one processes a map recursively
|
||||
// Format:
|
||||
// -> %parent_key%
|
||||
// -> %child_key0%: %child_value0%
|
||||
// -> %child_key1%
|
||||
// -> %nested_key0%: %nested_value0%
|
||||
// -> %nested_key1%: %nested_value1%
|
||||
// -> %child_key2%: %child_value1%
|
||||
//noinspection unchecked
|
||||
content.append(key).append("\n").append(processCrashContent((LinkedHashMap<Object, Object>) map.get(key), indentationSize + 1));
|
||||
}
|
||||
|
||||
return content.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the content for a crash report. Processes {@code crashContent} and spits out a String.
|
||||
*
|
||||
* @return crash content string
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public static String processCrashContent() {
|
||||
return processCrashContent(crashContent, 0);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.logging;
|
||||
|
||||
import de.staropensource.engine.base.EngineConfiguration;
|
||||
import de.staropensource.engine.base.internal.implementation.placeholder.logger.LogLevelEvent;
|
||||
import de.staropensource.engine.base.internal.implementation.placeholder.logger.LogPath;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Provides logging capabilities during engine startup.
|
||||
*
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
public final class InitLogger {
|
||||
/**
|
||||
* Contains the logging template used during startup.
|
||||
*
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
private static final @NotNull String template = "%log_level% %log_path% %log_message%";
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @since v1-alpha6
|
||||
*/
|
||||
private InitLogger() {}
|
||||
|
||||
/**
|
||||
* {@link Logger#log(LogLevel, Class, String, String, String)} and {@link Logger#processLogMessage(LogLevel, Class, String, String, String)} combined into one method.
|
||||
*
|
||||
* @param level level
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
private static synchronized void log(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
// Dismiss if level is not allowed
|
||||
if ((EngineConfiguration.getInstance() != null && level.compareTo(EngineConfiguration.getInstance().getLoggerLevel()) < 0) || level.compareTo(LogLevel.INFORMATIONAL) < 0)
|
||||
return;
|
||||
|
||||
// Invoke LoggingAdapter#prePlaceholder
|
||||
String base = Logger.getLoggingAdapter().prePlaceholder(level, issuerClass, issuerOrigin, issuerMetadata, message, template);
|
||||
if (base == null)
|
||||
base = template;
|
||||
|
||||
// Replace placeholders
|
||||
// This is done manually to avoid depending on PlaceholderEngine
|
||||
base = new LogLevelEvent(level).replace(base);
|
||||
base = new LogPath(issuerClass).replace(base);
|
||||
base = base.replace("%log_message%", message.replace("\n", ""));
|
||||
|
||||
// Invoke LoggingAdapter#postPlaceholder
|
||||
base = Logger.getLoggingAdapter().postPlaceholder(level, issuerClass, issuerOrigin, issuerMetadata, message, base);
|
||||
|
||||
// Print log message by invoking LoggingAdapter#print
|
||||
Logger.getLoggingAdapter().print(level, issuerClass, issuerOrigin, issuerMetadata, message, base);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a diagnostic message.
|
||||
*
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
public static void diag(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
log(LogLevel.DIAGNOSTIC, issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a verbose message.
|
||||
*
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
public static void verb(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
log(LogLevel.VERBOSE, issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a silent warning message.
|
||||
*
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
public static void sarn(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
log(LogLevel.SILENT_WARNING, issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints an informational message.
|
||||
*
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
public static void info(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
log(LogLevel.INFORMATIONAL, issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a warning message.
|
||||
*
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
public static void warn(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
log(LogLevel.WARNING, issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints an error message.
|
||||
*
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
public static void error(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
log(LogLevel.ERROR, issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crashes the entire engine.
|
||||
*
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @param throwable the throwable that caused this crash
|
||||
* @param handled declares the throwable has handled, not causing the engine to shutdown
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
public static void crash(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull Throwable throwable, boolean handled) {
|
||||
Logger.crash(issuerClass, issuerOrigin, issuerMetadata, message, throwable, handled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crashes the entire engine.
|
||||
*
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @param throwable the throwable that caused this crash
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
public static void crash(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull Throwable throwable) {
|
||||
Logger.crash(issuerClass, issuerOrigin, issuerMetadata, message, throwable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crashes the entire engine.
|
||||
*
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
public static void crash(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
Logger.crash(issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
}
|
||||
}
|
|
@ -19,31 +19,46 @@
|
|||
|
||||
package de.staropensource.engine.base.logging;
|
||||
|
||||
import de.staropensource.engine.base.Engine;
|
||||
import de.staropensource.engine.base.EngineConfiguration;
|
||||
import de.staropensource.engine.base.event.LogEvent;
|
||||
import de.staropensource.engine.base.implementable.LoggingAdapter;
|
||||
import de.staropensource.engine.base.implementable.helper.EventHelper;
|
||||
import de.staropensource.engine.base.implementation.logging.PlainLoggingAdapter;
|
||||
import de.staropensource.engine.base.internal.type.QueuedLogCall;
|
||||
import de.staropensource.engine.base.logging.backend.CrashHandler;
|
||||
import de.staropensource.engine.base.logging.backend.Filterer;
|
||||
import de.staropensource.engine.base.logging.backend.Processor;
|
||||
import de.staropensource.engine.base.logging.backend.async.LoggingQueue;
|
||||
import de.staropensource.engine.base.type.immutable.ImmutableArrayList;
|
||||
import de.staropensource.engine.base.implementation.shortcode.EmptyShortcodeConverter;
|
||||
import de.staropensource.engine.base.internal.implementation.placeholder.logger.*;
|
||||
import de.staropensource.engine.base.internal.type.QueuedLogMessage;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import de.staropensource.engine.base.type.logging.LogRule;
|
||||
import de.staropensource.engine.base.type.logging.LogRuleType;
|
||||
import de.staropensource.engine.base.utility.PlaceholderEngine;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.intellij.lang.annotations.RegExp;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* The frontend class for sos!engine's logging system.
|
||||
* Provides the engine's logging infrastructure, except for
|
||||
* crash handling, which is handled by {@link CrashHandler}.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @see LoggerInstance
|
||||
* @see CrashHandler
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@SuppressWarnings({ "JavadocDeclaration" })
|
||||
public final class Logger {
|
||||
/**
|
||||
* Contains a list of {@link QueuedLogMessage}s.
|
||||
*
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
private static final LinkedList<QueuedLogMessage> queuedMessages = new LinkedList<>();
|
||||
|
||||
/**
|
||||
* Refers to the active {@link LoggingAdapter} that is used to process and print log messages.
|
||||
*
|
||||
|
@ -67,350 +82,286 @@ public final class Logger {
|
|||
private static @NotNull LoggingAdapter loggingAdapter = new PlainLoggingAdapter();
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class
|
||||
* Contains all active {@link LogRule}s.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @since v1-alpha1
|
||||
* -- GETTER --
|
||||
* Returns all active {@link LogRule}s.
|
||||
*
|
||||
* @return active rules
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
@Getter
|
||||
private static final List<LogRule> activeRules = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @since v1-alpha6
|
||||
*/
|
||||
private Logger() {}
|
||||
|
||||
// -----> Internal management methods
|
||||
// These methods forward calls to internal methods so
|
||||
// these can be accessed without exporting a whole package.
|
||||
/**
|
||||
* Handles incoming log calls and either
|
||||
* processes them directly or queues them in.
|
||||
* <p>
|
||||
* **This is an internal method. Use with care.**
|
||||
* Prints all queued log messages.
|
||||
*
|
||||
* @param level level of the log call
|
||||
* @param issuer {@link StackTraceElement} of the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha8
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
public static void handle(@NotNull LogLevel level, @NotNull StackTraceElement issuer, @NotNull String message) {
|
||||
Processor.handle(level, issuer, message);
|
||||
public static synchronized void flushLogMessages() {
|
||||
// Only execute code if queued messages list is not empty
|
||||
if (!queuedMessages.isEmpty()) {
|
||||
// Clone queued messages and clear original to avoid issues
|
||||
//noinspection unchecked
|
||||
LinkedList<QueuedLogMessage> queuedMessagesCloned = (LinkedList<QueuedLogMessage>) queuedMessages.clone();
|
||||
queuedMessages.clear();
|
||||
|
||||
// Invoke processLogMessage method for every queued message
|
||||
for (QueuedLogMessage queuedLogMessage : queuedMessagesCloned) {
|
||||
processLogMessage(queuedLogMessage.level(), queuedLogMessage.issuerClass(), queuedLogMessage.issuerOrigin(), queuedLogMessage.issuerMetadata(), queuedLogMessage.message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the logging queue.
|
||||
* <p>
|
||||
* **This is an internal method. Use with care.**
|
||||
* Processes a log message.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @param level level
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
public static void flush() {
|
||||
LoggingQueue.flush();
|
||||
private static void processLogMessage(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
// Evaluate active rules
|
||||
for (LogRule rule : activeRules) {
|
||||
if (rule.evaluate(level, issuerClass, issuerOrigin, issuerMetadata, message)) {
|
||||
if (rule.getType() == LogRuleType.WHITELIST) break; // Continue processing
|
||||
if (rule.getType() == LogRuleType.BLACKLIST) return; // Cancel processing
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke LoggingAdapter#prePlaceholder
|
||||
String format = loggingAdapter.prePlaceholder(level, issuerClass, issuerOrigin, issuerMetadata, message, EngineConfiguration.getInstance().getLoggerTemplate());
|
||||
if (format == null)
|
||||
format = EngineConfiguration.getInstance().getLoggerTemplate();
|
||||
|
||||
// Replace placeholders using PlaceholderEngine
|
||||
format = PlaceholderEngine.getInstance().process(format);
|
||||
|
||||
// Replace logger placeholders (no colors)
|
||||
format = new LogClass(issuerClass).replace(format);
|
||||
format = new LogLevelEvent(level).replace(format);
|
||||
format = new LogMetadata(issuerMetadata).replace(format);
|
||||
format = new LogOrigin(issuerOrigin).replace(format);
|
||||
format = new LogPackage(issuerClass).replace(format);
|
||||
format = new LogPath(issuerClass).replace(format);
|
||||
|
||||
// Handle newlines
|
||||
if (EngineConfiguration.getInstance().isLoggerEnableNewlineSupport() && message.contains("\n"))
|
||||
try (Scanner scanner = new Scanner(message)) {
|
||||
int indexPrefix = format.indexOf("%log_message_prefix%");
|
||||
int indexMessage = format.indexOf("%log_message%");
|
||||
StringBuilder formatNew = new StringBuilder();
|
||||
String prefix;
|
||||
String suffix;
|
||||
String formatShadow;
|
||||
|
||||
if (indexMessage == -1)
|
||||
crash(Logger.class, "ENGINE", null, "The log format must contain %log_message%");
|
||||
|
||||
prefix = indexPrefix == -1 ? "" : format.substring(indexPrefix + 20, indexMessage);
|
||||
suffix = format.substring(indexMessage + 13);
|
||||
formatShadow = " "
|
||||
.repeat(new EmptyShortcodeConverter(
|
||||
format.substring(0, indexPrefix == -1 ? indexMessage : indexPrefix)
|
||||
.replace("%log_color_primary%", "")
|
||||
.replace("%log_color_secondary%", ""), false
|
||||
).getClean().length());
|
||||
|
||||
while (scanner.hasNextLine()) {
|
||||
if (formatNew.isEmpty())
|
||||
formatNew.append(format, 0, indexPrefix == -1 ? indexMessage : indexPrefix);
|
||||
else
|
||||
formatNew
|
||||
.append("\n")
|
||||
.append(formatShadow);
|
||||
|
||||
formatNew
|
||||
.append(prefix)
|
||||
.append(scanner.nextLine())
|
||||
.append(suffix);
|
||||
}
|
||||
|
||||
format = formatNew.toString();
|
||||
}
|
||||
else
|
||||
// No newline found, use performance-efficient replacing
|
||||
format = format
|
||||
.replace("%log_message_prefix%", "")
|
||||
.replace("%log_message%", message.replace("\n", "\\n"));
|
||||
|
||||
// Replace placeholders involving colors
|
||||
format = new LogColorPrimary(level).replace(format);
|
||||
format = new LogColorSecondary(level).replace(format);
|
||||
|
||||
// Replace placeholders using PlaceholderEngine again
|
||||
format = PlaceholderEngine.getInstance().process(format);
|
||||
|
||||
// Invoke LoggingAdapter#postPlaceholder
|
||||
format = loggingAdapter.postPlaceholder(level, issuerClass, issuerOrigin, issuerMetadata, message, format);
|
||||
|
||||
// Call event
|
||||
if (!(issuerClass.getName().equals("de.staropensource.engine.slf4j_compat.CompatibilityLogger")
|
||||
|| issuerClass.equals(EventHelper.class)))
|
||||
new LogEvent().callEvent(level, issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
|
||||
// Print log message by invoking LoggingAdapter#print
|
||||
loggingAdapter.print(level, issuerClass, issuerOrigin, issuerMetadata, message, format);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disallows one or multiple classes.
|
||||
* Handler for all log messages.
|
||||
*
|
||||
* @param regex regex
|
||||
* @since v1-alpha8
|
||||
* @param level level
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public static void disallowClass(@RegExp @NotNull String regex) {
|
||||
Filterer.disallowClass(regex);
|
||||
private static synchronized void log(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
// Check if engine has initialized
|
||||
if (Engine.getInstance() == null) return;
|
||||
|
||||
// Dismiss if level is not allowed
|
||||
if (level.compareTo(EngineConfiguration.getInstance().getLoggerLevel()) < 0)
|
||||
return;
|
||||
|
||||
if (EngineConfiguration.getInstance().isOptimizeLogging())
|
||||
// Optimizations enabled, add to message queue
|
||||
queuedMessages.add(new QueuedLogMessage(level, issuerClass, issuerOrigin, issuerMetadata, message));
|
||||
else
|
||||
// Optimizations disabled, print right away
|
||||
processLogMessage(level, issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disallows one or multiple modules.
|
||||
*
|
||||
* @param regex regex
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void disallowModule(@RegExp @NotNull String regex) {
|
||||
Filterer.disallowModule(regex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disallows one or multiple messages.
|
||||
*
|
||||
* @param regex regex
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void disallowMessage(@RegExp @NotNull String regex) {
|
||||
Filterer.disallowMessage(regex);
|
||||
}
|
||||
|
||||
|
||||
// -----> Redirection methods
|
||||
/**
|
||||
* Redirects regular log messages.
|
||||
*
|
||||
* @param level level of the log call
|
||||
* @param message message
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void redirectCall(@NotNull LogLevel level, @NotNull String message) {
|
||||
Processor.handle(level, Thread.currentThread().getStackTrace()[3], message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects crash calls.
|
||||
*
|
||||
* @param message message
|
||||
* @param throwable {@link Throwable} which caused the error
|
||||
* @param fatal if to terminate the engine
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void redirectCall(@NotNull String message, @Nullable Throwable throwable, boolean fatal) {
|
||||
CrashHandler.handleCrash(Thread.currentThread().getStackTrace()[3], message, throwable, fatal);
|
||||
}
|
||||
|
||||
|
||||
// -----> Frontend methods
|
||||
/**
|
||||
* Logs a diagnostic message.
|
||||
*
|
||||
* @param message message to log
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void diag(@NotNull String message) {
|
||||
redirectCall(LogLevel.DIAGNOSTIC, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a verbose message.
|
||||
*
|
||||
* @param message message to log
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void verb(@NotNull String message) {
|
||||
redirectCall(LogLevel.VERBOSE, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a silent warning.
|
||||
*
|
||||
* @param message message to log
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void sarn(@NotNull String message) {
|
||||
redirectCall(LogLevel.SILENT_WARNING, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an informational message.
|
||||
*
|
||||
* @param message message to log
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void info(@NotNull String message) {
|
||||
redirectCall(LogLevel.INFORMATIONAL, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a warning.
|
||||
*
|
||||
* @param message message to log
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void warn(@NotNull String message) {
|
||||
redirectCall(LogLevel.WARNING, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an error.
|
||||
*
|
||||
* @param message message to log
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void error(@NotNull String message) {
|
||||
redirectCall(LogLevel.ERROR, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a crash report and optionally crashes the engine.
|
||||
*
|
||||
* @param message message to log
|
||||
* @param throwable {@link Throwable} which caused the crash
|
||||
* @param fatal terminates the engine if {@code true}
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void crash(@NotNull String message, @NotNull Throwable throwable, boolean fatal) {
|
||||
redirectCall(message, throwable, fatal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a crash report and optionally crashes the engine.
|
||||
*
|
||||
* @param message message to log
|
||||
* @param throwable {@link Throwable} which caused the crash
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void crash(@NotNull String message, @NotNull Throwable throwable) {
|
||||
redirectCall(message, throwable, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a crash report and optionally crashes the engine.
|
||||
*
|
||||
* @param message message to log
|
||||
* @param fatal terminates the engine if {@code true}
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void crash(@NotNull String message, boolean fatal) {
|
||||
redirectCall(message, null, fatal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a crash report and optionally crashes the engine.
|
||||
*
|
||||
* @param message message to log
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void crash(@NotNull String message) {
|
||||
redirectCall(message, null, true);
|
||||
}
|
||||
|
||||
|
||||
// -----> Legacy frontend methods
|
||||
// This improves compatibility with old code
|
||||
// still using the old logger frontend.
|
||||
/**
|
||||
* Prints a diagnostic message.
|
||||
*
|
||||
* @param ignoredIssuerClass class of the issuer
|
||||
* @param ignoredIssuerOrigin origin of the issuer
|
||||
* @param ignoredIssuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @deprecated The old logging system has been deprecated and replaced by a new one
|
||||
* @see #diag(String)
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void diag(@NotNull Class<?> ignoredIssuerClass, @NotNull String ignoredIssuerOrigin, @Nullable String ignoredIssuerMetadata, @NotNull String message) {
|
||||
redirectCall(LogLevel.DIAGNOSTIC, message);
|
||||
public static void diag(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
log(LogLevel.DIAGNOSTIC, issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a verbose message.
|
||||
*
|
||||
* @param ignoredIssuerClass class of the issuer
|
||||
* @param ignoredIssuerOrigin origin of the issuer
|
||||
* @param ignoredIssuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @deprecated The old logging system has been deprecated and replaced by a new one
|
||||
* @see #verb(String)
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void verb(@NotNull Class<?> ignoredIssuerClass, @NotNull String ignoredIssuerOrigin, @Nullable String ignoredIssuerMetadata, @NotNull String message) {
|
||||
redirectCall(LogLevel.VERBOSE, message);
|
||||
public static void verb(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
log(LogLevel.VERBOSE, issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a silent warning message.
|
||||
*
|
||||
* @param ignoredIssuerClass class of the issuer
|
||||
* @param ignoredIssuerOrigin origin of the issuer
|
||||
* @param ignoredIssuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @deprecated The old logging system has been deprecated and replaced by a new one
|
||||
* @see #sarn(String)
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void sarn(@NotNull Class<?> ignoredIssuerClass, @NotNull String ignoredIssuerOrigin, @Nullable String ignoredIssuerMetadata, @NotNull String message) {
|
||||
redirectCall(LogLevel.SILENT_WARNING, message);
|
||||
public static void sarn(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
log(LogLevel.SILENT_WARNING, issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints an informational message.
|
||||
*
|
||||
* @param ignoredIssuerClass class of the issuer
|
||||
* @param ignoredIssuerOrigin origin of the issuer
|
||||
* @param ignoredIssuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @deprecated The old logging system has been deprecated and replaced by a new one
|
||||
* @see #info(String)
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void info(@NotNull Class<?> ignoredIssuerClass, @NotNull String ignoredIssuerOrigin, @Nullable String ignoredIssuerMetadata, @NotNull String message) {
|
||||
redirectCall(LogLevel.INFORMATIONAL, message);
|
||||
public static void info(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
log(LogLevel.INFORMATIONAL, issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a warning message.
|
||||
*
|
||||
* @param ignoredIssuerClass class of the issuer
|
||||
* @param ignoredIssuerOrigin origin of the issuer
|
||||
* @param ignoredIssuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @deprecated The old logging system has been deprecated and replaced by a new one
|
||||
* @see #warn(String)
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void warn(@NotNull Class<?> ignoredIssuerClass, @NotNull String ignoredIssuerOrigin, @Nullable String ignoredIssuerMetadata, @NotNull String message) {
|
||||
redirectCall(LogLevel.WARNING, message);
|
||||
public static void warn(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
log(LogLevel.WARNING, issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints an error message.
|
||||
*
|
||||
* @param ignoredIssuerClass class of the issuer
|
||||
* @param ignoredIssuerOrigin origin of the issuer
|
||||
* @param ignoredIssuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @deprecated The old logging system has been deprecated and replaced by a new one
|
||||
* @see #error(String)
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void error(@NotNull Class<?> ignoredIssuerClass, @NotNull String ignoredIssuerOrigin, @Nullable String ignoredIssuerMetadata, @NotNull String message) {
|
||||
redirectCall(LogLevel.ERROR, message);
|
||||
public static void error(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
log(LogLevel.ERROR, issuerClass, issuerOrigin, issuerMetadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crashes the entire engine.
|
||||
*
|
||||
* @param ignoredIssuerClass class of the issuer
|
||||
* @param ignoredIssuerOrigin origin of the issuer
|
||||
* @param ignoredIssuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @param throwable the throwable that caused this crash
|
||||
* @param handled declares the throwable has handled, not causing the engine to shutdown
|
||||
* @deprecated The old logging system has been deprecated and replaced by a new one
|
||||
* @see #crash(String, Throwable, boolean)
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @param throwable the throwable that caused this crash
|
||||
* @param handled declares the throwable has handled, not causing the engine to shutdown
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void crash(@NotNull Class<?> ignoredIssuerClass, @NotNull String ignoredIssuerOrigin, @Nullable String ignoredIssuerMetadata, @NotNull String message, @NotNull Throwable throwable, boolean handled) {
|
||||
redirectCall(message, throwable, !handled);
|
||||
public static void crash(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull Throwable throwable, boolean handled) {
|
||||
CrashHandler.handleCrash(issuerClass, issuerOrigin, issuerMetadata, message, throwable, handled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crashes the entire engine.
|
||||
*
|
||||
* @param ignoredIssuerClass class of the issuer
|
||||
* @param ignoredIssuerOrigin origin of the issuer
|
||||
* @param ignoredIssuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @param throwable the throwable that caused this crash
|
||||
* @deprecated The old logging system has been deprecated and replaced by a new one
|
||||
* @see #crash(String, Throwable)
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @param throwable the throwable that caused this crash
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void crash(@NotNull Class<?> ignoredIssuerClass, @NotNull String ignoredIssuerOrigin, @Nullable String ignoredIssuerMetadata, @NotNull String message, @NotNull Throwable throwable) {
|
||||
redirectCall(message, throwable, true);
|
||||
public static void crash(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message, @NotNull Throwable throwable) {
|
||||
CrashHandler.handleCrash(issuerClass, issuerOrigin, issuerMetadata, message, throwable, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crashes the entire engine.
|
||||
*
|
||||
* @param ignoredIssuerClass class of the issuer
|
||||
* @param ignoredIssuerOrigin origin of the issuer
|
||||
* @param ignoredIssuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @deprecated The old logging system has been deprecated and replaced by a new one
|
||||
* @see #crash(String)
|
||||
* @param issuerClass class of the issuer
|
||||
* @param issuerOrigin origin of the issuer
|
||||
* @param issuerMetadata metadata about the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
public static void crash(@NotNull Class<?> ignoredIssuerClass, @NotNull String ignoredIssuerOrigin, @Nullable String ignoredIssuerMetadata, @NotNull String message) {
|
||||
redirectCall(message, null, true);
|
||||
public static void crash(@NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
CrashHandler.handleCrash(issuerClass, issuerOrigin, issuerMetadata, message, null, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,29 +19,69 @@
|
|||
|
||||
package de.staropensource.engine.base.logging;
|
||||
|
||||
import de.staropensource.engine.base.Engine;
|
||||
import de.staropensource.engine.base.type.EngineState;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Legacy frontend.
|
||||
* <p>
|
||||
* This improves compatibility with old code
|
||||
* still using the old logger frontend.
|
||||
* Removes the need to add {@code getClass} to all calls to {@link Logger}.
|
||||
*
|
||||
* @deprecated The old logging system has been deprecated and replaced by a new one
|
||||
* @see Logger
|
||||
* @since v1-alpha8
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@Getter
|
||||
@Deprecated(forRemoval = true)
|
||||
@SuppressWarnings({ "JavadocDeclaration" })
|
||||
public final class LoggerInstance {
|
||||
/**
|
||||
* Contains the {@link Class} of the issuer.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
* -- GETTER --
|
||||
* Returns the {@link Class} of the issuer.
|
||||
*
|
||||
* @return class of the issuer
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private final @NotNull Class<?> clazz;
|
||||
|
||||
/**
|
||||
* Contains the metadata about the issuer.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
* -- GETTER --
|
||||
* Returns the metadata about the issuer.
|
||||
*
|
||||
* @return metadata about the issuer
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private final @Nullable String metadata;
|
||||
|
||||
/**
|
||||
* Contains the origin of the issuer.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
* -- GETTER --
|
||||
* Returns the origin of the issuer.
|
||||
*
|
||||
* @return origin of the issuer
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private final @NotNull String origin;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @param clazz class of the issuer
|
||||
* @param origin origin of the issuer
|
||||
* @param metadata metadata about the issuer
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private LoggerInstance() {}
|
||||
private LoggerInstance(@NotNull Class<?> clazz, @NotNull String origin, @Nullable String metadata) {
|
||||
this.clazz = clazz;
|
||||
this.origin = origin;
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a diagnostic message.
|
||||
|
@ -50,7 +90,10 @@ public final class LoggerInstance {
|
|||
* @since v1-alpha0
|
||||
*/
|
||||
public void diag(@NotNull String message) {
|
||||
Logger.diag(message);
|
||||
if (Engine.getInstance().getState() == EngineState.EARLY_STARTUP)
|
||||
InitLogger.diag(clazz, origin, metadata, message);
|
||||
else
|
||||
Logger.diag(clazz, origin, metadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,7 +103,10 @@ public final class LoggerInstance {
|
|||
* @since v1-alpha0
|
||||
*/
|
||||
public void verb(@NotNull String message) {
|
||||
Logger.verb(message);
|
||||
if (Engine.getInstance().getState() == EngineState.EARLY_STARTUP)
|
||||
InitLogger.verb(clazz, origin, metadata, message);
|
||||
else
|
||||
Logger.verb(clazz, origin, metadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,7 +116,10 @@ public final class LoggerInstance {
|
|||
* @since v1-alpha0
|
||||
*/
|
||||
public void sarn(@NotNull String message) {
|
||||
Logger.sarn(message);
|
||||
if (Engine.getInstance().getState() == EngineState.EARLY_STARTUP)
|
||||
InitLogger.sarn(clazz, origin, metadata, message);
|
||||
else
|
||||
Logger.sarn(clazz, origin, metadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,7 +129,10 @@ public final class LoggerInstance {
|
|||
* @since v1-alpha0
|
||||
*/
|
||||
public void info(@NotNull String message) {
|
||||
Logger.info(message);
|
||||
if (Engine.getInstance().getState() == EngineState.EARLY_STARTUP)
|
||||
InitLogger.info(clazz, origin, metadata, message);
|
||||
else
|
||||
Logger.info(clazz, origin, metadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,7 +142,10 @@ public final class LoggerInstance {
|
|||
* @since v1-alpha0
|
||||
*/
|
||||
public void warn(@NotNull String message) {
|
||||
Logger.warn(message);
|
||||
if (Engine.getInstance().getState() == EngineState.EARLY_STARTUP)
|
||||
InitLogger.warn(clazz, origin, metadata, message);
|
||||
else
|
||||
Logger.warn(clazz, origin, metadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -100,7 +155,10 @@ public final class LoggerInstance {
|
|||
* @since v1-alpha0
|
||||
*/
|
||||
public void error(@NotNull String message) {
|
||||
Logger.error(message);
|
||||
if (Engine.getInstance().getState() == EngineState.EARLY_STARTUP)
|
||||
InitLogger.error(clazz, origin, metadata, message);
|
||||
else
|
||||
Logger.error(clazz, origin, metadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,10 +167,11 @@ public final class LoggerInstance {
|
|||
* @param message message
|
||||
* @param throwable throwable that caused the crash
|
||||
* @param handled declares the throwable has handled
|
||||
* @see CrashHandler
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public void crash(@NotNull String message, @NotNull Throwable throwable, boolean handled) {
|
||||
Logger.crash(message, throwable, !handled);
|
||||
Logger.crash(clazz, origin, metadata, message, throwable, handled);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,50 +179,52 @@ public final class LoggerInstance {
|
|||
*
|
||||
* @param message message
|
||||
* @param throwable throwable that caused the crash
|
||||
* @see CrashHandler
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public void crash(@NotNull String message, @NotNull Throwable throwable) {
|
||||
Logger.crash(message, throwable);
|
||||
Logger.crash(clazz, origin, metadata, message, throwable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crashes the entire engine.
|
||||
*
|
||||
* @param message message
|
||||
* @see CrashHandler
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public void crash(@NotNull String message) {
|
||||
Logger.crash(message);
|
||||
Logger.crash(clazz, origin, metadata, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an API for building {@link LoggerInstance}s more easily.
|
||||
*
|
||||
* @deprecated The old logging system has been deprecated and replaced by a new one
|
||||
* @see Logger
|
||||
* @since v1-alpha8
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@Deprecated(forRemoval = true)
|
||||
@SuppressWarnings({ "unused" })
|
||||
public static final class Builder {
|
||||
/**
|
||||
* Contains the class of the issuer.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @see LoggerInstance#clazz
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private @Nullable Class<?> clazz = null;
|
||||
|
||||
/**
|
||||
* Contains the origin of the issuer.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @see LoggerInstance#origin
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private @Nullable String origin = null;
|
||||
|
||||
/**
|
||||
* Contains metadata about the issuer.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @see LoggerInstance#metadata
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private @Nullable String metadata = null;
|
||||
|
||||
|
@ -192,13 +253,14 @@ public final class LoggerInstance {
|
|||
if (metadata == null || metadata.isBlank())
|
||||
metadata = null;
|
||||
|
||||
return new LoggerInstance();
|
||||
return new LoggerInstance(clazz, origin, metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the class of the issuer.
|
||||
*
|
||||
* @return class of the issuer
|
||||
* @see LoggerInstance#clazz
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public @Nullable Class<?> getClazz() {
|
||||
|
@ -209,6 +271,7 @@ public final class LoggerInstance {
|
|||
* Returns the origin of the issuer.
|
||||
*
|
||||
* @return origin of the issuer
|
||||
* @see LoggerInstance#origin
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public @Nullable String getOrigin() {
|
||||
|
@ -219,6 +282,7 @@ public final class LoggerInstance {
|
|||
* Returns metadata about the issuer.
|
||||
*
|
||||
* @return metadata about the issuer
|
||||
* @see LoggerInstance#metadata
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public @Nullable String getMetadata() {
|
||||
|
@ -230,6 +294,7 @@ public final class LoggerInstance {
|
|||
*
|
||||
* @param clazz new class of the issuer
|
||||
* @return builder instance
|
||||
* @see LoggerInstance#clazz
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public @NotNull Builder setClazz(@Nullable Class<?> clazz) {
|
||||
|
@ -242,6 +307,7 @@ public final class LoggerInstance {
|
|||
*
|
||||
* @param origin new origin of the issuer
|
||||
* @return builder instance
|
||||
* @see LoggerInstance#origin
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public @NotNull Builder setOrigin(@Nullable String origin) {
|
||||
|
@ -254,6 +320,7 @@ public final class LoggerInstance {
|
|||
*
|
||||
* @param metadata new metadata about the issuer
|
||||
* @return builder instance
|
||||
* @see LoggerInstance#metadata
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public @NotNull Builder setMetadata(@Nullable String metadata) {
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.logging;
|
||||
|
||||
import de.staropensource.engine.base.Engine;
|
||||
import de.staropensource.engine.base.EngineConfiguration;
|
||||
import de.staropensource.engine.base.type.EngineState;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Controls the logging thread of the engine.
|
||||
*
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
@Slf4j
|
||||
public class LoggingThread {
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
protected static final @NotNull LoggerInstance logger = new LoggerInstance.Builder().setClazz(LoggingThread.class).setOrigin("ENGINE").build();
|
||||
|
||||
/**
|
||||
* Contains the logic of the logging thread.
|
||||
*
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
private static final @NotNull Runnable loggingThreadLogic = () -> {
|
||||
while (!Thread.currentThread().isInterrupted()) {
|
||||
// Stop thread when engine is shutting down
|
||||
if (Engine.getInstance().getState() == EngineState.SHUTDOWN
|
||||
|| Engine.getInstance().getState() == EngineState.CRASHED
|
||||
|| !EngineConfiguration.getInstance().isOptimizeLogging())
|
||||
return;
|
||||
|
||||
// Process all log messages
|
||||
Logger.flushLogMessages();
|
||||
|
||||
// Sleep for whatever has been configured
|
||||
if (EngineConfiguration.getInstance().getLoggerPollingSpeed() > 0) {
|
||||
long sleepDuration = System.currentTimeMillis() + EngineConfiguration.getInstance().getLoggerPollingSpeed();
|
||||
while (System.currentTimeMillis() < sleepDuration)
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains a reference to the logging thread.
|
||||
*
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
private static Thread loggingThread;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class
|
||||
*
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
private LoggingThread() {}
|
||||
|
||||
/**
|
||||
* Reconstructs the {@link #loggingThread} thread.
|
||||
*
|
||||
* @throws IllegalStateException if the logging thread's state is not {@link Thread.State#TERMINATED}
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
private static void reconstructThread() throws IllegalStateException {
|
||||
if (loggingThread != null && loggingThread.getState() != Thread.State.TERMINATED)
|
||||
throw new IllegalStateException("The logging thread needs to be terminated before reconstruction");
|
||||
|
||||
loggingThread = Thread
|
||||
.ofPlatform()
|
||||
.daemon()
|
||||
.name("Logging thread")
|
||||
.group(Engine.getThreadGroup())
|
||||
.stackSize(10)
|
||||
.unstarted(loggingThreadLogic);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the logging thread.
|
||||
*
|
||||
* @since v1-alpha4
|
||||
* @throws IllegalStateException if thread reconstruction fails because the thread isn't {@link Thread.State#TERMINATED}, see {@link #reconstructThread()}
|
||||
* @see #loggingThread
|
||||
*/
|
||||
public static void startThread() {
|
||||
if (loggingThread == null)
|
||||
reconstructThread();
|
||||
|
||||
if (loggingThread.isAlive()) {
|
||||
// Executing the thread restart logic prevents blocking the current thread
|
||||
// while still ensuring that the logging thread is properly restarted
|
||||
Thread
|
||||
.ofVirtual()
|
||||
.name("Logging thread restart thread")
|
||||
.start(() -> {
|
||||
logger.diag("Restarting the logging thread");
|
||||
|
||||
loggingThread.interrupt();
|
||||
|
||||
// Make sure that the logging thread is dead before reconstructing and starting it
|
||||
while (loggingThread.isAlive())
|
||||
Thread.onSpinWait();
|
||||
|
||||
reconstructThread();
|
||||
loggingThread.start();
|
||||
});
|
||||
} else
|
||||
loggingThread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Thread.State} of the logging thread.
|
||||
*
|
||||
* @return logging thread state
|
||||
* @since v1-alpha4
|
||||
* @see Thread.State
|
||||
*/
|
||||
public static @NotNull Thread.State getState() {
|
||||
return loggingThread.getState();
|
||||
}
|
||||
}
|
|
@ -19,8 +19,6 @@
|
|||
|
||||
package de.staropensource.engine.base.logging;
|
||||
|
||||
import de.staropensource.engine.base.Engine;
|
||||
import de.staropensource.engine.base.type.EngineState;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -36,18 +34,26 @@ import java.nio.charset.StandardCharsets;
|
|||
*
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
@SuppressWarnings({ "unused", "JavadocDeclaration" })
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class PrintStreamService {
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
private static final @NotNull LoggerInstance logger = new LoggerInstance.Builder().setClazz(PrintStreamService.class).setOrigin("ENGINE").build();
|
||||
|
||||
/**
|
||||
* Contains the diagnostic stream.
|
||||
* Anything sent will be redirected to
|
||||
* {@link Logger#info(String)}
|
||||
* {@link Logger#info(Class, String, String, String)}
|
||||
*
|
||||
* @since v1-alpha4
|
||||
* -- GETTER --
|
||||
* Returns the diagnostic stream.
|
||||
* Anything sent will be redirected to
|
||||
* {@link Logger#info(String)}
|
||||
* {@link Logger#info(Class, String, String, String)}
|
||||
*
|
||||
* @return diagnostic stream
|
||||
* @since v1-alpha4
|
||||
|
@ -58,13 +64,13 @@ public final class PrintStreamService {
|
|||
/**
|
||||
* Contains the verbose stream.
|
||||
* Anything sent will be redirected to
|
||||
* {@link Logger#error(String)}
|
||||
* {@link Logger#error(Class, String, String, String)}
|
||||
*
|
||||
* @since v1-alpha4
|
||||
* -- GETTER --
|
||||
* Returns the verbose stream.
|
||||
* Anything sent will be redirected to
|
||||
* {@link Logger#error(String)}
|
||||
* {@link Logger#error(Class, String, String, String)}
|
||||
*
|
||||
* @return verbose stream
|
||||
* @since v1-alpha4
|
||||
|
@ -75,13 +81,13 @@ public final class PrintStreamService {
|
|||
/**
|
||||
* Contains the silent warning stream.
|
||||
* Anything sent will be redirected to
|
||||
* {@link Logger#error(String)}
|
||||
* {@link Logger#error(Class, String, String, String)}
|
||||
*
|
||||
* @since v1-alpha4
|
||||
* -- GETTER --
|
||||
* Returns the silent warning stream.
|
||||
* Anything sent will be redirected to
|
||||
* {@link Logger#error(String)}
|
||||
* {@link Logger#error(Class, String, String, String)}
|
||||
*
|
||||
* @return silent warning stream
|
||||
* @since v1-alpha4
|
||||
|
@ -92,13 +98,13 @@ public final class PrintStreamService {
|
|||
/**
|
||||
* Contains the informational stream.
|
||||
* Anything sent will be redirected to
|
||||
* {@link Logger#info(String)}
|
||||
* {@link Logger#info(Class, String, String, String)}
|
||||
*
|
||||
* @since v1-alpha4
|
||||
* -- GETTER --
|
||||
* Returns the informational stream.
|
||||
* Anything sent will be redirected to
|
||||
* {@link Logger#info(String)}
|
||||
* {@link Logger#info(Class, String, String, String)}
|
||||
*
|
||||
* @return informational stream
|
||||
* @since v1-alpha4
|
||||
|
@ -109,13 +115,13 @@ public final class PrintStreamService {
|
|||
/**
|
||||
* Contains the warning stream.
|
||||
* Anything sent will be redirected to
|
||||
* {@link Logger#error(String)}
|
||||
* {@link Logger#error(Class, String, String, String)}
|
||||
*
|
||||
* @since v1-alpha4
|
||||
* -- GETTER --
|
||||
* Returns the warning stream.
|
||||
* Anything sent will be redirected to
|
||||
* {@link Logger#error(String)}
|
||||
* {@link Logger#error(Class, String, String, String)}
|
||||
*
|
||||
* @return warning stream
|
||||
* @since v1-alpha4
|
||||
|
@ -126,13 +132,13 @@ public final class PrintStreamService {
|
|||
/**
|
||||
* Contains the error stream.
|
||||
* Anything sent will be redirected to
|
||||
* {@link Logger#info(String)}
|
||||
* {@link Logger#info(Class, String, String, String)}
|
||||
*
|
||||
* @since v1-alpha4
|
||||
* -- GETTER --
|
||||
* Returns the error stream.
|
||||
* Anything sent will be redirected to
|
||||
* {@link Logger#info(String)}
|
||||
* {@link Logger#info(Class, String, String, String)}
|
||||
*
|
||||
* @return error stream
|
||||
* @since v1-alpha4
|
||||
|
@ -143,13 +149,13 @@ public final class PrintStreamService {
|
|||
/**
|
||||
* Contains the crash stream.
|
||||
* Anything sent will be redirected to
|
||||
* {@link Logger#error(String)}
|
||||
* {@link Logger#error(Class, String, String, String)}
|
||||
*
|
||||
* @since v1-alpha4
|
||||
* -- GETTER --
|
||||
* Returns the crash stream.
|
||||
* Anything sent will be redirected to
|
||||
* {@link Logger#error(String)}
|
||||
* {@link Logger#error(Class, String, String, String)}
|
||||
*
|
||||
* @return crash stream
|
||||
* @since v1-alpha4
|
||||
|
@ -166,14 +172,18 @@ public final class PrintStreamService {
|
|||
|
||||
/**
|
||||
* Initializes all {@link PrintStream}s offered by this class.
|
||||
* <p>
|
||||
* Only works during early engine startup.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
public static void initializeStreams() {
|
||||
if (Engine.getInstance() == null || Engine.getInstance().getState() != EngineState.EARLY_STARTUP)
|
||||
return;
|
||||
// Close all existing streams
|
||||
if (diag != null) diag.close();
|
||||
if (verb != null) verb.close();
|
||||
if (sarn != null) sarn.close();
|
||||
if (info != null) info.close();
|
||||
if (warn != null) warn.close();
|
||||
if (error != null) error.close();
|
||||
if (crash != null) crash.close();
|
||||
|
||||
// Create streams
|
||||
diag = LogStream.createPrintStream(LogLevel.DIAGNOSTIC);
|
||||
|
@ -267,13 +277,13 @@ public final class PrintStreamService {
|
|||
// Check for newline
|
||||
if (sequence.indexOf("\n") != -1) {
|
||||
switch (level) {
|
||||
case DIAGNOSTIC -> Logger.diag(sequence.toString());
|
||||
case VERBOSE -> Logger.verb(sequence.toString());
|
||||
case SILENT_WARNING -> Logger.sarn(sequence.toString());
|
||||
case INFORMATIONAL -> Logger.info(sequence.toString());
|
||||
case WARNING -> Logger.warn(sequence.toString());
|
||||
case ERROR -> Logger.error(sequence.toString());
|
||||
case CRASH -> Logger.crash(sequence.toString());
|
||||
case DIAGNOSTIC -> logger.diag(sequence.toString());
|
||||
case VERBOSE -> logger.verb(sequence.toString());
|
||||
case SILENT_WARNING -> logger.sarn(sequence.toString());
|
||||
case INFORMATIONAL -> logger.info(sequence.toString());
|
||||
case WARNING -> logger.warn(sequence.toString());
|
||||
case ERROR -> logger.error(sequence.toString());
|
||||
case CRASH -> logger.crash(sequence.toString());
|
||||
}
|
||||
|
||||
sequence = new StringBuilder();
|
||||
|
|
|
@ -1,338 +0,0 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.logging.backend;
|
||||
|
||||
import de.staropensource.engine.base.Engine;
|
||||
import de.staropensource.engine.base.EngineConfiguration;
|
||||
import de.staropensource.engine.base.EngineInternals;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import de.staropensource.engine.base.utility.Math;
|
||||
import de.staropensource.engine.base.utility.Miscellaneous;
|
||||
import de.staropensource.engine.base.utility.information.EngineInformation;
|
||||
import de.staropensource.engine.base.utility.information.JvmInformation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static de.staropensource.engine.base.logging.backend.Processor.isFeatureEnabled;
|
||||
|
||||
/**
|
||||
* Handles crashes.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public final class CrashHandler {
|
||||
/**
|
||||
* Contains all random witty comments.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static final @NotNull String[] WITTYCOMMENTS = new String[]{
|
||||
"Who fucked up here?",
|
||||
"What is it now?",
|
||||
":neofox_woozy:",
|
||||
"Oh no!",
|
||||
"my engine brokey brokey",
|
||||
"weird",
|
||||
"Is this a feature?",
|
||||
"$ git blame",
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private CrashHandler() {}
|
||||
|
||||
/**
|
||||
* Handles crash reports.
|
||||
*
|
||||
* @param issuer {@link StackTraceElement} of the issuer
|
||||
* @param message message
|
||||
* @param throwable {@link Throwable} which caused the error
|
||||
* @param fatal if to terminate the engine
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void handleCrash(@NotNull StackTraceElement issuer, @NotNull String message, @Nullable Throwable throwable, boolean fatal) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
String outputFinal;
|
||||
|
||||
// Header
|
||||
output
|
||||
.append("==== sos!engine crash report ====")
|
||||
.append("\nDear user: The application or game you just used seems to have run into a problem.")
|
||||
.append("\n Please be so kind and report this crash report to the developers so they can fix the issue.")
|
||||
.append("\nDear developer: FIX YOUR SHIT. If the engine is at fault here, please report the crash")
|
||||
.append("\n to StarOpenSource here: https://git.staropensource.de/StarOpenSource/Engine/issues")
|
||||
.append("\n\n// ")
|
||||
.append(WITTYCOMMENTS[new Random().nextInt(WITTYCOMMENTS.length)])
|
||||
.append("\n\n");
|
||||
|
||||
// Information about the crash
|
||||
output
|
||||
.append("---- Crash Information ----\n")
|
||||
.append("Issuer: ")
|
||||
.append(issuer.getClassName())
|
||||
.append("@")
|
||||
.append(issuer.getModuleName())
|
||||
.append("#")
|
||||
.append(issuer.getMethodName())
|
||||
.append("~")
|
||||
.append(issuer.getLineNumber())
|
||||
|
||||
.append("\nFatal: ")
|
||||
.append(fatal);
|
||||
|
||||
if (throwable == null)
|
||||
output
|
||||
.append("\nCaused by:")
|
||||
.append("\nNo throwable has been passed.");
|
||||
else
|
||||
output
|
||||
.append("\n")
|
||||
.append(Miscellaneous.getStackTraceHeader(throwable))
|
||||
.append("\n")
|
||||
.append(Miscellaneous.getStackTraceAsString(throwable, true))
|
||||
.append("\n");
|
||||
|
||||
output.append("\nMessage: \n")
|
||||
.append(message)
|
||||
.append("\n\n");
|
||||
|
||||
// Environment information
|
||||
output
|
||||
.append("---- Environment ----")
|
||||
.append("\nTime and date: ")
|
||||
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.DAY_OF_MONTH), 2))
|
||||
.append(".")
|
||||
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.MONTH), 2))
|
||||
.append(".")
|
||||
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.YEAR), 4))
|
||||
.append(" ")
|
||||
.append(de.staropensource.engine.base.utility.Math.padNumbers(Calendar.getInstance().get(Calendar.HOUR_OF_DAY), 2))
|
||||
.append(":")
|
||||
.append(de.staropensource.engine.base.utility.Math.padNumbers(Calendar.getInstance().get(Calendar.MINUTE), 2))
|
||||
.append(":")
|
||||
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.SECOND), 2))
|
||||
.append(" [")
|
||||
.append(TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT, Locale.US))
|
||||
.append("]")
|
||||
|
||||
.append("\nUNIX Epoch: ")
|
||||
.append(Math.padNumbers(System.currentTimeMillis(), String.valueOf(Long.MAX_VALUE).length()))
|
||||
|
||||
.append("\nOperating system: ")
|
||||
.append(System.getProperty("os.name"))
|
||||
|
||||
.append("\nCPU architecture: ")
|
||||
.append(System.getProperty("os.arch"))
|
||||
|
||||
.append("\nMemory: ")
|
||||
.append(JvmInformation.getMemoryUsed() / 1024)
|
||||
.append(" MiB")
|
||||
.append("/")
|
||||
.append(JvmInformation.getMemoryLimit() / 1024)
|
||||
.append(" MiB")
|
||||
.append(" (stack ")
|
||||
.append(JvmInformation.getMemoryStack().getUsed() / 1024)
|
||||
.append(" MiB")
|
||||
.append("/")
|
||||
.append(JvmInformation.getMemoryStack().getMax() == -1 ? "∞" : JvmInformation.getMemoryStack().getMax() / 1024)
|
||||
.append(" MiB")
|
||||
.append(", heap ")
|
||||
.append(JvmInformation.getMemoryHeap().getUsed() / 1024)
|
||||
.append(" MiB")
|
||||
.append("/")
|
||||
.append(JvmInformation.getMemoryHeap().getMax() == -1 ? "∞" : JvmInformation.getMemoryStack().getMax() / 1024)
|
||||
.append(" MiB)")
|
||||
|
||||
.append("\nJVM: ")
|
||||
.append(JvmInformation.getImplementationName())
|
||||
.append(" ")
|
||||
.append(JvmInformation.getImplementationVersion())
|
||||
.append(" @ ")
|
||||
.append(JvmInformation.getJavaVersion())
|
||||
.append(" by ")
|
||||
.append(JvmInformation.getImplementationVendor())
|
||||
|
||||
.append("\nJVM arguments: ");
|
||||
|
||||
for (String argument : JvmInformation.getArguments())
|
||||
output
|
||||
.append("\n- '")
|
||||
.append(argument)
|
||||
.append("'");
|
||||
|
||||
output.append("\n\n");
|
||||
|
||||
// Engine
|
||||
output.append("---- sos!engine ----\n");
|
||||
|
||||
if (EngineInformation.getVersioningString() == null)
|
||||
output.append("EngineInformation is not yet initialized");
|
||||
else
|
||||
output
|
||||
.append("Version: ")
|
||||
.append(EngineInformation.getVersioningString())
|
||||
|
||||
.append("\nCommit: ")
|
||||
.append(EngineInformation.getGitCommitIdentifierLong())
|
||||
|
||||
.append("\nDirty: ")
|
||||
.append(EngineInformation.isGitDirty());
|
||||
|
||||
output.append("\n\n");
|
||||
|
||||
// Engine configuration
|
||||
output.append("---- sos!engine configuration ----\n");
|
||||
|
||||
if (EngineConfiguration.getInstance() == null)
|
||||
output.append("EngineConfiguration is not yet initialized");
|
||||
else
|
||||
output
|
||||
.append("EngineConfiguration#debug='")
|
||||
.append(EngineConfiguration.getInstance().isDebug())
|
||||
.append("'\n")
|
||||
|
||||
.append("EngineConfiguration#debugEvents='")
|
||||
.append(EngineConfiguration.getInstance().isDebugEvents())
|
||||
.append("'\n")
|
||||
|
||||
.append("EngineConfiguration#initialPerformSubsystemInitialization='")
|
||||
.append(EngineConfiguration.getInstance().isInitialPerformSubsystemInitialization())
|
||||
.append("'\n")
|
||||
|
||||
.append("EngineConfiguration#initialIncludeSubsystemClasses='")
|
||||
.append(EngineConfiguration.getInstance().getInitialIncludeSubsystemClasses())
|
||||
.append("'\n")
|
||||
|
||||
.append("EngineConfiguration#errorShortcodeParser='")
|
||||
.append(EngineConfiguration.getInstance().isErrorShortcodeParser())
|
||||
.append("'\n")
|
||||
|
||||
.append("EngineConfiguration#optimizeLogging='")
|
||||
.append(EngineConfiguration.getInstance().isOptimizeLogging())
|
||||
.append("'\n")
|
||||
|
||||
.append("EngineConfiguration#optimizeEvents='")
|
||||
.append(EngineConfiguration.getInstance().isOptimizeEvents())
|
||||
.append("'\n")
|
||||
|
||||
.append("EngineConfiguration#logLevel='")
|
||||
.append(EngineConfiguration.getInstance().getLogLevel().name())
|
||||
.append("'\n")
|
||||
|
||||
.append("EngineConfiguration#logSettings='")
|
||||
.append(EngineConfiguration.getInstance().getLogFeatures())
|
||||
.append("'\n")
|
||||
|
||||
.append("EngineConfiguration#logPollingSpeed='")
|
||||
.append(EngineConfiguration.getInstance().getLogPollingSpeed())
|
||||
.append("'\n")
|
||||
|
||||
.append("EngineConfiguration#logForceStandardOutput='")
|
||||
.append(EngineConfiguration.getInstance().isLogForceStandardOutput())
|
||||
.append("'\n")
|
||||
|
||||
.append("EngineConfiguration#hideFullTypePath='")
|
||||
.append(EngineConfiguration.getInstance().isHideFullTypePath());
|
||||
|
||||
output.append("'\n\n");
|
||||
|
||||
// System properties
|
||||
output.append("---- System properties ----");
|
||||
|
||||
for (String property : System.getProperties().stringPropertyNames().stream().sorted().toList())
|
||||
output
|
||||
.append("\n")
|
||||
.append(property)
|
||||
.append("='")
|
||||
.append(System.getProperties().getProperty(property).replace("\n", "\\n"))
|
||||
.append("'");
|
||||
|
||||
output.append("\n\n");
|
||||
|
||||
// Stacktraces of all threads
|
||||
output.append("---- Stacktraces of all threads ----");
|
||||
{
|
||||
Map<Thread, StackTraceElement[]> stacktraces = Thread.getAllStackTraces();
|
||||
|
||||
for (Thread thread : stacktraces.keySet())
|
||||
output
|
||||
.append("\n")
|
||||
.append(thread.getName())
|
||||
.append(" (id=")
|
||||
.append(thread.threadId())
|
||||
.append(" priority=")
|
||||
.append(thread.getPriority())
|
||||
.append(" group=")
|
||||
.append(thread.getThreadGroup() == null ? "<unknown>" : thread.getThreadGroup().getName())
|
||||
.append(" state=")
|
||||
.append(thread.getState().name())
|
||||
.append(" daemon=")
|
||||
.append(thread.isDaemon())
|
||||
.append("):")
|
||||
.append("\n")
|
||||
.append(Miscellaneous.getStackTraceAsString(stacktraces.get(thread), false))
|
||||
.append("\n");
|
||||
}
|
||||
output.append("\n");
|
||||
|
||||
// Footer
|
||||
output
|
||||
.append("Dear user: The application or game you just used seems to have run into a problem.\n")
|
||||
.append(" Please be so kind and report this crash report to the developers so they can fix the issue.\n")
|
||||
.append("Dear developer: FIX YOUR SHIT. If the engine is at fault here, please report the crash\n")
|
||||
.append(" to StarOpenSource here: https://git.staropensource.de/StarOpenSource/Engine/issues\n")
|
||||
.append("==== sos!engine crash report ====");
|
||||
|
||||
// Formatting
|
||||
if (isFeatureEnabled("formatting")) {
|
||||
outputFinal =
|
||||
"<fg:red><bold>"
|
||||
+ output
|
||||
.toString()
|
||||
.replace("<", "\\<")
|
||||
+ "<reset>";
|
||||
} else
|
||||
outputFinal = output.toString();
|
||||
|
||||
// Print
|
||||
Logger.getLoggingAdapter().print(LogLevel.CRASH, issuer, message, outputFinal);
|
||||
|
||||
// Terminate engine
|
||||
// We do a test on the engine state here
|
||||
// to prevent bugs and multiple engine shutdowns.
|
||||
if (fatal)
|
||||
switch (Engine.getInstance().getState()) {
|
||||
case UNKNOWN, EARLY_STARTUP, STARTUP -> {
|
||||
if (EngineInternals.getInstance() == null)
|
||||
Runtime.getRuntime().exit(69);
|
||||
else
|
||||
EngineInternals.getInstance().getShutdownHandler().shutdown((short) 69);
|
||||
}
|
||||
case RUNNING -> Engine.getInstance().shutdown(69);
|
||||
case SHUTDOWN, CRASHED -> {}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.logging.backend;
|
||||
|
||||
import org.intellij.lang.annotations.RegExp;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Responsible for filtering log messages out.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public final class Filterer {
|
||||
/**
|
||||
* Contains a list of all disallowed classes.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
static @NotNull List<@NotNull String> disallowedClasses = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Contains a list of all disallowed modules.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
static @NotNull List<@NotNull String> disallowedModules = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Contains a list of all disallowed messages.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
static @NotNull List<@NotNull String> disallowedMessages = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private Filterer() {}
|
||||
|
||||
/**
|
||||
* Disallows one or multiple classes.
|
||||
*
|
||||
* @param regex regex
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void disallowClass(@RegExp @NotNull String regex) {
|
||||
disallowedClasses.add(regex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disallows one or multiple modules.
|
||||
*
|
||||
* @param regex regex
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void disallowModule(@RegExp @NotNull String regex) {
|
||||
disallowedModules.add(regex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Disallows one or multiple messages.
|
||||
*
|
||||
* @param regex regex
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void disallowMessage(@RegExp @NotNull String regex) {
|
||||
disallowedMessages.add(regex);
|
||||
}
|
||||
}
|
|
@ -1,375 +0,0 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.logging.backend;
|
||||
|
||||
import de.staropensource.engine.base.EngineConfiguration;
|
||||
import de.staropensource.engine.base.implementation.shortcode.EmptyShortcodeParser;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.logging.backend.async.LoggingQueue;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import de.staropensource.engine.base.utility.Math;
|
||||
import de.staropensource.engine.base.utility.PlaceholderEngine;
|
||||
import de.staropensource.engine.base.utility.information.JvmInformation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
* Processes log messages.
|
||||
*
|
||||
* @see #handle(LogLevel, StackTraceElement, String)
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public final class Processor {
|
||||
/**
|
||||
* Creates and initializes an instance of this class
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private Processor() {}
|
||||
|
||||
/**
|
||||
* Checks whether the specified feature is enabled.
|
||||
*
|
||||
* @param feature feature to check
|
||||
* @return enabled?
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static boolean isFeatureEnabled(@NotNull String feature) {
|
||||
return EngineConfiguration.getInstance() != null && EngineConfiguration.getInstance().getLogFeatures().contains(feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles incoming log calls and either
|
||||
* processes them directly or queues them in.
|
||||
*
|
||||
* @param level level of the log call
|
||||
* @param issuer {@link StackTraceElement} of the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void handle(@NotNull LogLevel level, @NotNull StackTraceElement issuer, @NotNull String message) {
|
||||
if (EngineConfiguration.getInstance() != null && EngineConfiguration.getInstance().isOptimizeLogging())
|
||||
LoggingQueue.add(level, issuer, message);
|
||||
else
|
||||
process(level, issuer, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a log call.
|
||||
*
|
||||
* @param level level of the log call
|
||||
* @param issuer {@link StackTraceElement} of the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void process(@NotNull LogLevel level, @NotNull StackTraceElement issuer, @NotNull String message) {
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
// Filter out
|
||||
if (EngineConfiguration.getInstance() == null) {
|
||||
LogLevel maxLevel = LogLevel.INFORMATIONAL;
|
||||
|
||||
try {
|
||||
maxLevel = LogLevel.valueOf(System.getProperties().getProperty("sosengine.base.logLevel", "informational").toUpperCase());
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
Logger.error("The log level '" + System.getProperties().getProperty("sosengine.base.logLevel", "informational") + "' is not valid");
|
||||
}
|
||||
|
||||
if (level.compareTo(maxLevel) < 0)
|
||||
return;
|
||||
} else if (level.compareTo(EngineConfiguration.getInstance().getLogLevel()) < 0)
|
||||
return;
|
||||
|
||||
for (String classNameDisallowed : Filterer.disallowedClasses)
|
||||
if (issuer.getClassName().matches(classNameDisallowed))
|
||||
return;
|
||||
for (String moduleNameDisallowed : Filterer.disallowedModules)
|
||||
if (issuer.getModuleName().matches(moduleNameDisallowed))
|
||||
return;
|
||||
for (String messageDisallowed : Filterer.disallowedModules)
|
||||
if (message.matches(messageDisallowed))
|
||||
return;
|
||||
|
||||
format(output, level);
|
||||
runtime(output);
|
||||
if (isFeatureEnabled("date") || isFeatureEnabled("time")) {
|
||||
output.append("[");
|
||||
date(output);
|
||||
if (isFeatureEnabled("date"))
|
||||
output.append(" ");
|
||||
time(output);
|
||||
output.append("] ");
|
||||
}
|
||||
output.append("[");
|
||||
level(output, level);
|
||||
format(output, level);
|
||||
output.append(" ");
|
||||
issuerClass(output, level, issuer);
|
||||
issuerModule(output, level, issuer);
|
||||
methodName(output, level, issuer);
|
||||
lineNumber(output, level, issuer);
|
||||
output.append("] ");
|
||||
message(output, message);
|
||||
format(output, "reset");
|
||||
|
||||
// Print
|
||||
Logger.getLoggingAdapter().print(level, issuer, message, output.toString());
|
||||
}
|
||||
|
||||
// -----> Formatting
|
||||
/**
|
||||
* Adds the {@code formatting} feature.
|
||||
* <p>
|
||||
* This method will reset and then color the following
|
||||
* substring in the log level-specific color.
|
||||
*
|
||||
* @param builder {@link StringBuilder} instance to append to
|
||||
* @param level level of the log call
|
||||
* @see #format(StringBuilder, String)
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static void format(@NotNull StringBuilder builder, @NotNull LogLevel level) {
|
||||
format(builder, "reset");
|
||||
format(builder, switch (level) {
|
||||
case DIAGNOSTIC, VERBOSE -> "fg:blue";
|
||||
case SILENT_WARNING, WARNING -> "fg:yellow";
|
||||
case INFORMATIONAL -> "fg:white";
|
||||
case ERROR -> "fg:red";
|
||||
case CRASH -> "you should not see this";
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {@code formatting} feature.
|
||||
*
|
||||
* @param builder {@link StringBuilder} instance to append to
|
||||
* @param component formatting component
|
||||
* @see #format(StringBuilder, LogLevel)
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static void format(@NotNull StringBuilder builder, @NotNull String component) {
|
||||
if (isFeatureEnabled("formatting"))
|
||||
builder
|
||||
.append("<")
|
||||
.append(component)
|
||||
.append(">");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {@code formatting} feature.
|
||||
*
|
||||
* @param string string to sanitize
|
||||
* @return sanitized string
|
||||
* @see #format(StringBuilder, LogLevel)
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static @NotNull String sanitizeFormat(@NotNull String string) {
|
||||
if (isFeatureEnabled("formatting"))
|
||||
return string.replace("<", "\\<");
|
||||
else
|
||||
return string;
|
||||
}
|
||||
|
||||
// -----> Features and components
|
||||
/**
|
||||
* Adds the {@code runtime} feature.
|
||||
*
|
||||
* @param builder {@link StringBuilder} instance to append to
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static void runtime(@NotNull StringBuilder builder) {
|
||||
if (isFeatureEnabled("runtime"))
|
||||
builder
|
||||
.append("[")
|
||||
.append(JvmInformation.getUptime())
|
||||
.append("ms")
|
||||
.append("] ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {@code time} feature.
|
||||
*
|
||||
* @param builder {@link StringBuilder} instance to append to
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static void time(@NotNull StringBuilder builder) {
|
||||
if (isFeatureEnabled("time"))
|
||||
builder
|
||||
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.HOUR_OF_DAY), 2))
|
||||
.append(":")
|
||||
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.MINUTE), 2))
|
||||
.append(":")
|
||||
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.SECOND), 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {@code date} feature.
|
||||
*
|
||||
* @param builder {@link StringBuilder} instance to append to
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static void date(@NotNull StringBuilder builder) {
|
||||
if (isFeatureEnabled("date"))
|
||||
builder
|
||||
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.DAY_OF_MONTH), 2))
|
||||
.append(".")
|
||||
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.MONTH), 2))
|
||||
.append(".")
|
||||
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.YEAR), 4));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {@code level} component.
|
||||
*
|
||||
* @param builder {@link StringBuilder} instance to append to
|
||||
* @param level level of the log call
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static void level(@NotNull StringBuilder builder, @NotNull LogLevel level) {
|
||||
format(builder, "bold");
|
||||
|
||||
builder.append(switch (level) {
|
||||
case DIAGNOSTIC -> "DIAG";
|
||||
case VERBOSE -> "VERB";
|
||||
case SILENT_WARNING -> "SARN";
|
||||
case INFORMATIONAL -> "INFO";
|
||||
case WARNING -> "WARN";
|
||||
case ERROR -> "ERR!";
|
||||
case CRASH -> "CRSH";
|
||||
});
|
||||
|
||||
format(builder, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {@code issuer class} component.
|
||||
*
|
||||
* @param builder {@link StringBuilder} instance to append to
|
||||
* @param level level of the log call
|
||||
* @param issuer {@link StackTraceElement} of the issuer
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static void issuerClass(@NotNull StringBuilder builder, @NotNull LogLevel level, @NotNull StackTraceElement issuer) {
|
||||
format(builder, "bold");
|
||||
|
||||
if (isFeatureEnabled("shortIssuerClass")) {
|
||||
String[] classNameSplit = issuer.getClassName().split("\\.");
|
||||
builder.append(classNameSplit[classNameSplit.length - 1]);
|
||||
} else
|
||||
builder.append(issuer.getClassName());
|
||||
|
||||
format(builder, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {@code moduleName} and {@code moduleVersion} features.
|
||||
*
|
||||
* @param builder {@link StringBuilder} instance to append to
|
||||
* @param level level of the log call
|
||||
* @param issuer {@link StackTraceElement} of the issuer
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static void issuerModule(@NotNull StringBuilder builder, @NotNull LogLevel level, @NotNull StackTraceElement issuer) {
|
||||
if (isFeatureEnabled("moduleName") && issuer.getModuleName() != null) {
|
||||
format(builder, "bold");
|
||||
|
||||
builder
|
||||
.append("@")
|
||||
.append(issuer.getModuleName());
|
||||
|
||||
if (isFeatureEnabled("moduleVersion") && issuer.getModuleVersion() != null)
|
||||
builder
|
||||
.append("v")
|
||||
.append(issuer.getModuleVersion());
|
||||
|
||||
format(builder, level);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {@code methodName} feature.
|
||||
*
|
||||
* @param builder {@link StringBuilder} instance to append to
|
||||
* @param level level of the log call
|
||||
* @param issuer {@link StackTraceElement} of the issuer
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static void methodName(@NotNull StringBuilder builder, @NotNull LogLevel level, @NotNull StackTraceElement issuer) {
|
||||
if (isFeatureEnabled("methodName")) {
|
||||
builder.append("#");
|
||||
format(builder, "bold");
|
||||
builder.append(sanitizeFormat(issuer.getMethodName()));
|
||||
format(builder, level);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {@code lineNumber} feature.
|
||||
*
|
||||
* @param builder {@link StringBuilder} instance to append to
|
||||
* @param level level of the log call
|
||||
* @param issuer {@link StackTraceElement} of the issuer
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static void lineNumber(@NotNull StringBuilder builder, @NotNull LogLevel level, @NotNull StackTraceElement issuer) {
|
||||
if (isFeatureEnabled("lineNumber")) {
|
||||
builder.append("~");
|
||||
format(builder, "bold");
|
||||
builder.append(issuer.getLineNumber());
|
||||
format(builder, level);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the {@code message} component.
|
||||
*
|
||||
* @param builder {@link StringBuilder} instance to append to
|
||||
* @param message message
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static void message(@NotNull StringBuilder builder, @NotNull String message) {
|
||||
builder.append(sanitizeFormat(handlePlaceholders(
|
||||
message.replace(
|
||||
"\n",
|
||||
"\n" + " ".repeat(new EmptyShortcodeParser(builder.toString(), true).getClean().length())
|
||||
)
|
||||
)));
|
||||
}
|
||||
|
||||
// -----> Utility methods
|
||||
/**
|
||||
* Uses the {@link PlaceholderEngine} to replace
|
||||
* all placeholders within a specified string and
|
||||
* returns it's result. The original string will
|
||||
* be returned if the {@link PlaceholderEngine}
|
||||
* is not yet initialized.
|
||||
*
|
||||
* @param string string to use
|
||||
* @return updated string
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static @NotNull String handlePlaceholders(@NotNull String string) {
|
||||
if (PlaceholderEngine.getInstance() == null)
|
||||
return string;
|
||||
else
|
||||
return PlaceholderEngine.getInstance().process(string);
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.logging.backend.async;
|
||||
|
||||
import de.staropensource.engine.base.internal.type.QueuedLogCall;
|
||||
import de.staropensource.engine.base.logging.backend.Processor;
|
||||
import de.staropensource.engine.base.type.immutable.ImmutableArrayList;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Handles everything related to the logging queue.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public final class LoggingQueue {
|
||||
/**
|
||||
* Contains the logging queue.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static final @NotNull List<@NotNull QueuedLogCall> queue = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private LoggingQueue() {}
|
||||
|
||||
/**
|
||||
* Adds a new entry to the logging queue.
|
||||
*
|
||||
* @param level level of the log call
|
||||
* @param issuer {@link StackTraceElement} of the issuer
|
||||
* @param message message
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void add(@NotNull LogLevel level, @NotNull StackTraceElement issuer, @NotNull String message) {
|
||||
queue.add(new QueuedLogCall(level, issuer, message));
|
||||
}
|
||||
|
||||
/**
|
||||
* Flushes the logging queue.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void flush() {
|
||||
// Get copy of and clear queue
|
||||
List<@NotNull QueuedLogCall> queue = new ImmutableArrayList<>(LoggingQueue.queue);
|
||||
LoggingQueue.queue.clear();
|
||||
|
||||
for (QueuedLogCall queuedCall : queue)
|
||||
Processor.process(queuedCall.level(), queuedCall.issuer(), queuedCall.message());
|
||||
}
|
||||
}
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.logging.backend.async;
|
||||
|
||||
import de.staropensource.engine.base.Engine;
|
||||
import de.staropensource.engine.base.EngineConfiguration;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.type.EngineState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Handles the logging thread.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public final class LoggingThread {
|
||||
/**
|
||||
* Contains the logging thread.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static Thread thread;
|
||||
|
||||
/**
|
||||
* Contains the code of the logging thread.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static final @NotNull Runnable threadCode = () -> {
|
||||
int pollingSpeed;
|
||||
|
||||
while (!(
|
||||
Thread.currentThread().isInterrupted()
|
||||
|| !(EngineConfiguration.getInstance() == null || EngineConfiguration.getInstance().isOptimizeLogging())
|
||||
|| Engine.getInstance().getState() == EngineState.SHUTDOWN
|
||||
|| Engine.getInstance().getState() == EngineState.CRASHED
|
||||
)) {
|
||||
if (EngineConfiguration.getInstance() == null)
|
||||
pollingSpeed = 5;
|
||||
else
|
||||
pollingSpeed = EngineConfiguration.getInstance().getLogPollingSpeed();
|
||||
|
||||
// Flush all log messages
|
||||
LoggingQueue.flush();
|
||||
|
||||
// Sleep for whatever has been configured
|
||||
if (pollingSpeed > 0) {
|
||||
long sleepDuration = System.currentTimeMillis() + pollingSpeed;
|
||||
while (System.currentTimeMillis() < sleepDuration)
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static {
|
||||
constructThread();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private LoggingThread() {}
|
||||
|
||||
/**
|
||||
* Constructs the logging thread.
|
||||
*
|
||||
* @see #thread
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static void constructThread() {
|
||||
thread = Thread
|
||||
.ofPlatform()
|
||||
.daemon()
|
||||
.name("Logging thread")
|
||||
.group(Engine.getThreadGroup())
|
||||
.priority(Thread.MAX_PRIORITY)
|
||||
.stackSize(10)
|
||||
.unstarted(threadCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* (Re-)Starts the logging thread.
|
||||
*
|
||||
* @param allowRestart if the logging thread should be restarted if it's stopped
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void startThread(boolean allowRestart) {
|
||||
if (allowRestart && thread.isAlive()) {
|
||||
// Executing the restart logic in another thread prevents
|
||||
// this thread from being fully blocked while still ensuring
|
||||
// that the logging thread is properly restarted
|
||||
Thread
|
||||
.ofVirtual()
|
||||
.name("Logging thread restart thread")
|
||||
.start(() -> {
|
||||
Logger.diag("Restarting the logging thread");
|
||||
|
||||
// Interrupt thread
|
||||
// This let's our thread code know that it should terminate
|
||||
thread.interrupt();
|
||||
|
||||
// Make sure that the logging thread is dead before reconstructing and starting it
|
||||
while (thread.isAlive())
|
||||
Thread.onSpinWait();
|
||||
|
||||
constructThread();
|
||||
thread.start();
|
||||
});
|
||||
} else
|
||||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Thread.State} of the logging thread.
|
||||
*
|
||||
* @return logging thread state
|
||||
* @see Thread.State
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static @NotNull Thread.State getState() {
|
||||
return thread.getState();
|
||||
}
|
||||
}
|
|
@ -18,9 +18,11 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* The engine's logging infrastructure.
|
||||
* The engine's custom logging infrastructure.
|
||||
*
|
||||
* @see de.staropensource.engine.base.logging.Logger
|
||||
* @see de.staropensource.engine.base.logging.LoggerInstance
|
||||
* @see de.staropensource.engine.base.logging.CrashHandler
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
package de.staropensource.engine.base.logging;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package de.staropensource.engine.base.reflection;
|
||||
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -39,6 +39,14 @@ import java.util.Map;
|
|||
* @since v1-alpha2
|
||||
*/
|
||||
public final class ClasspathAccess {
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private static final @NotNull LoggerInstance logger = new LoggerInstance.Builder().setClazz(ClasspathAccess.class).setOrigin("ENGINE").build();
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
|
@ -61,7 +69,7 @@ public final class ClasspathAccess {
|
|||
try {
|
||||
urls.add(new File(path).toURI().toURL());
|
||||
} catch (Exception exception) {
|
||||
Logger.crash("Failed converting classpath to URL", exception);
|
||||
logger.crash("Failed converting classpath to URL", exception);
|
||||
}
|
||||
|
||||
return fixURLs(urls);
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
|
||||
package de.staropensource.engine.base.type;
|
||||
|
||||
import de.staropensource.engine.base.logging.InitLogger;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
|
||||
/**
|
||||
* Determines in which state the engine is currently in.
|
||||
*
|
||||
|
@ -26,52 +29,58 @@ package de.staropensource.engine.base.type;
|
|||
*/
|
||||
public enum EngineState {
|
||||
/**
|
||||
* Indicates that the state of the engine is
|
||||
* currently unknown. The engine is most likely
|
||||
* not initialized yet.
|
||||
* The state of the engine is currently unknown.
|
||||
* The engine likely has not been initialized yet.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
UNKNOWN,
|
||||
|
||||
/**
|
||||
* Indicates that the engine is largely
|
||||
* uninitialized and unsafe to use.
|
||||
* The engine is in early startup.
|
||||
* The engine is largely uninitialized and
|
||||
* the logging infrastructure is not yet ready
|
||||
* to be used.
|
||||
* <p>
|
||||
* This state is used to indicate that the
|
||||
* {@link InitLogger} shall be used instead
|
||||
* of {@link Logger}.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
EARLY_STARTUP,
|
||||
|
||||
/**
|
||||
* Indicates that the core engine has fully
|
||||
* initialized, except for all subsystems.
|
||||
* The engine is starting up.
|
||||
* Some parts of the engine may not be initialized
|
||||
* yet and may be unsafe to access. The logging
|
||||
* infrastructure is ready to be used though and is
|
||||
* safe to access.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
STARTUP,
|
||||
|
||||
/**
|
||||
* Indicates that the engine is running
|
||||
* and operating normally.
|
||||
* The engine is in normal operation mode.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
RUNNING,
|
||||
|
||||
/**
|
||||
* Indicates that the engine or the
|
||||
* application has crashed.
|
||||
* The engine has crashed, either by itself or the application.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
CRASHED,
|
||||
|
||||
/**
|
||||
* Indicates that the engine is shutting
|
||||
* down. The engine is unusable in this
|
||||
* state and should not be used.
|
||||
* The engine is shutting down.
|
||||
* Some parts of the engine may have already
|
||||
* shut down and may be unsafe to access.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
SHUTDOWN,
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ public abstract class LogRule {
|
|||
private final @NotNull LogRuleType type;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this abstract class.
|
||||
* Initializes this abstract class.
|
||||
*
|
||||
* @param type rule type
|
||||
*/
|
||||
|
|
|
@ -22,7 +22,7 @@ package de.staropensource.engine.base.utility;
|
|||
import de.staropensource.engine.base.exception.dependency.DependencyCycleException;
|
||||
import de.staropensource.engine.base.exception.dependency.UnmetDependenciesException;
|
||||
import de.staropensource.engine.base.implementable.VersioningSystem;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import de.staropensource.engine.base.type.DependencyVector;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
@ -39,6 +39,14 @@ import java.util.*;
|
|||
*/
|
||||
@SuppressWarnings({ "unused", "UnusedReturnValue", "JavadocDeclaration" })
|
||||
public final class DependencyResolver {
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
private final @NotNull LoggerInstance logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("ENGINE").setMetadata(String.valueOf(hashCode())).build();
|
||||
|
||||
/**
|
||||
* List of {@link DependencyVector}s to resolve.
|
||||
*
|
||||
|
@ -267,7 +275,7 @@ public final class DependencyResolver {
|
|||
try {
|
||||
versioningSystemResolved = vectorDependency.getVersioningSystem().getDeclaredConstructor(String.class).newInstance(vectorDependency.getVersion());
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException exception) {
|
||||
Logger.crash("Unable to check version of dependency \"" + dependency + "\": Unable to initialize versioning system " + vectorDependency.getVersioningSystem().getName(), exception);
|
||||
logger.crash("Unable to check version of dependency \"" + dependency + "\": Unable to initialize versioning system " + vectorDependency.getVersioningSystem().getName(), exception);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -279,7 +287,7 @@ public final class DependencyResolver {
|
|||
try {
|
||||
versioningSystemEquals = vectorDependency.getVersioningSystem().getDeclaredConstructor(String.class).newInstance(versionEqual.toString());
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException exception) {
|
||||
Logger.crash("Unable to check version of dependency \"" + dependency + "\": Unable to initialize versioning system " + vectorDependency.getVersioningSystem().getName(), exception);
|
||||
logger.crash("Unable to check version of dependency \"" + dependency + "\": Unable to initialize versioning system " + vectorDependency.getVersioningSystem().getName(), exception);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -295,7 +303,7 @@ public final class DependencyResolver {
|
|||
try {
|
||||
versioningSystemSmaller = vectorDependency.getVersioningSystem().getDeclaredConstructor(String.class).newInstance(versionSmaller.toString());
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException exception) {
|
||||
Logger.crash("Unable to check version of dependency \"" + dependency + "\": Unable to initialize versioning system " + vectorDependency.getVersioningSystem().getName(), exception);
|
||||
logger.crash("Unable to check version of dependency \"" + dependency + "\": Unable to initialize versioning system " + vectorDependency.getVersioningSystem().getName(), exception);
|
||||
break;
|
||||
}
|
||||
if (!versionBigger.isEmpty())
|
||||
|
@ -303,7 +311,7 @@ public final class DependencyResolver {
|
|||
try {
|
||||
versioningSystemBigger = vectorDependency.getVersioningSystem().getDeclaredConstructor(String.class).newInstance(versionBigger.toString());
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException exception) {
|
||||
Logger.crash("Unable to check version of dependency \"" + dependency + "\": Unable to initialize versioning system " + vectorDependency.getVersioningSystem().getName(), exception);
|
||||
logger.crash("Unable to check version of dependency \"" + dependency + "\": Unable to initialize versioning system " + vectorDependency.getVersioningSystem().getName(), exception);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,851 +0,0 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.base.utility;
|
||||
|
||||
import de.staropensource.engine.base.Engine;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.type.EngineState;
|
||||
import de.staropensource.engine.base.type.FileType;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.PosixFilePermissions;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Provides a simplified way of
|
||||
* accessing files and directories.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
@Getter
|
||||
@SuppressWarnings({ "JavadocDeclaration", "unused" })
|
||||
public final class FileAccess {
|
||||
// -----> Static variables
|
||||
/**
|
||||
* Contains a list of all files and directories
|
||||
* which should be deleted at shutdown.
|
||||
* <p>
|
||||
* While this feature is built into Java, in
|
||||
* our testing it did not seem to work correctly.
|
||||
* That's why we're implementing it here.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private static @NotNull Path[] scheduledDeletion = new Path[0];
|
||||
|
||||
/**
|
||||
* Contains a {@link FileAccess} instance to
|
||||
* a cache directory provided by the engine.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* -- GETTER --
|
||||
* Returns a {@link FileAccess} instance to
|
||||
* a cache directory provided by the engine.
|
||||
*
|
||||
* @return cache directory
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
@Getter
|
||||
private static FileAccess cacheDirectory;
|
||||
|
||||
// -----> Instance variables
|
||||
/**
|
||||
* Contains the {@link Path} to this file or directory.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* -- GETTER --
|
||||
* Returns the {@link Path} to this file or directory.
|
||||
* <p>
|
||||
* Please only use this method when you have to.
|
||||
* Use the methods in this class instead, if you can.
|
||||
*
|
||||
* @return associated {@link Path} instance
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private final @NotNull Path path;
|
||||
|
||||
/**
|
||||
* Contains the {@link File} to this file or directory.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* -- GETTER --
|
||||
* Returns the {@link File} to this file or directory.
|
||||
* <p>
|
||||
* Please only use this method when you have to.
|
||||
* Use the methods in this class instead, if you can.
|
||||
*
|
||||
* @return associated {@link File} instance
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
private final @NotNull File file;
|
||||
|
||||
// -----> Constructors
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @param path path you wish to access
|
||||
* @throws InvalidPathException if a {@link Path} cannot be created (see {@link FileSystem#getPath(String, String...)})
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public FileAccess(@NotNull String path) throws InvalidPathException {
|
||||
this.path = formatPath(path).toAbsolutePath();
|
||||
this.file = new File(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @param path path you wish to access
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public FileAccess(@NotNull Path path) {
|
||||
this.path = path.toAbsolutePath();
|
||||
this.file = this.path.toFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @param file {@link File} to the path you wish to access
|
||||
* @throws InvalidPathException if a {@link Path} cannot be created (see {@link FileSystem#getPath(String, String...)})
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public FileAccess(@NotNull File file) throws InvalidPathException {
|
||||
this.path = file.toPath().toAbsolutePath();
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @param uri {@link URI} to the path you wish to access
|
||||
* @throws FileSystemNotFoundException if the filesystem is not supported by Java
|
||||
* @throws IllegalArgumentException if the URI is improperly formatted
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public FileAccess(@NotNull URI uri) throws FileSystemNotFoundException, IllegalArgumentException {
|
||||
this.path = Path.of(uri);
|
||||
this.file = new File(uri);
|
||||
}
|
||||
|
||||
|
||||
// -----> Static instance initialization
|
||||
/**
|
||||
* Initializes all uninitialized static
|
||||
* {@link FileAccess} instances.
|
||||
* <p>
|
||||
* Only works during early engine startup.
|
||||
*
|
||||
* @throws IOException on an IO error
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void initializeInstances() throws IOException {
|
||||
if (Engine.getInstance().getState() == EngineState.EARLY_STARTUP)
|
||||
cacheDirectory = new FileAccess(System.getProperties().getProperty("java.io.tmpdir").replace("\\", "/") + "/sosengine-cache-" + ProcessHandle.current().pid()).createDirectory().deleteOnShutdown();
|
||||
}
|
||||
/**
|
||||
* Deletes all files scheduled for deletion.
|
||||
* <p>
|
||||
* Only works during engine shutdown.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static void deleteScheduled() {
|
||||
if (Engine.getInstance().getState() == EngineState.SHUTDOWN || Engine.getInstance().getState() == EngineState.CRASHED) {
|
||||
Logger.verb("Deleting files scheduled for deletion on shutdown");
|
||||
|
||||
for (Path path : scheduledDeletion)
|
||||
try (Stream<Path> stream = Files.walk(path)) {
|
||||
Logger.diag("Deleting file or directory \"" + path + "\"");
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
stream.sorted(Comparator.reverseOrder()).map(Path::toFile).forEach(File::delete);
|
||||
|
||||
if (Files.exists(path))
|
||||
Logger.error("Deleting file or directory \"" + path + "\" failed");
|
||||
} catch (Exception exception) {
|
||||
Logger.error("File or directory \"" + path + "\" could not be deleted\n" + Miscellaneous.getStackTraceHeader(exception) + "\n" + Miscellaneous.getStackTraceAsString(exception, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----> Path formatting
|
||||
/**
|
||||
* Formats the path into a {@link Path} instance.
|
||||
*
|
||||
* @param path path to format
|
||||
* @return formatted {@link Path}
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static @NotNull Path formatPath(@NotNull String path) {
|
||||
return Path.of(
|
||||
path
|
||||
.replace("/./", "/")
|
||||
.replace("/", File.separator)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unformats the a {@link Path} instance into a string.
|
||||
*
|
||||
* @param path {@link Path} to unformat
|
||||
* @return unformatted path
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public static @NotNull String unformatPath(@NotNull Path path) {
|
||||
return path
|
||||
.toString()
|
||||
.replace(File.separator, "/");
|
||||
}
|
||||
|
||||
|
||||
// -----> File getters & setters
|
||||
/**
|
||||
* Returns the absolute path of this file.
|
||||
*
|
||||
* @return absolute path
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull String getPath() {
|
||||
return unformatPath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file name.
|
||||
*
|
||||
* @param excludeExtension if to remove the extension (e.g. {@code .txt}, {@code .java})
|
||||
* @return file name
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull String getFileName(boolean excludeExtension) {
|
||||
if (excludeExtension)
|
||||
return file.getName().replaceFirst("[.][^.]+$", "");
|
||||
else
|
||||
return file.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this file exists.
|
||||
*
|
||||
* @return exists?
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public boolean exists() {
|
||||
return Files.exists(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of this file.
|
||||
*
|
||||
* @return file type
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull FileType getType() {
|
||||
if (!exists())
|
||||
return FileType.VOID;
|
||||
else if (Files.isRegularFile(path))
|
||||
return FileType.FILE;
|
||||
else if (Files.isDirectory(path))
|
||||
return FileType.DIRECTORY;
|
||||
else
|
||||
return FileType.UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not this file is a symbolic link.
|
||||
*
|
||||
* @return symbolic link?
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public boolean isSymbolicLink() {
|
||||
return Files.isSymbolicLink(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the file is hidden.
|
||||
*
|
||||
* @return is hidden?
|
||||
* @throws IOException on an IO error
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public boolean isHidden() throws IOException {
|
||||
return Files.isHidden(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of all files and
|
||||
* directories in this directory.
|
||||
*
|
||||
* @return array of file and directory names
|
||||
* @throws UnsupportedOperationException if the file is not a directory
|
||||
* @throws IOException on an IO error
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull String @NotNull [] listContents() throws UnsupportedOperationException, IOException {
|
||||
if (getType() != FileType.DIRECTORY)
|
||||
throw new UnsupportedOperationException("The file \"" + path + "\" is not a directory");
|
||||
|
||||
String[] list = file.list();
|
||||
|
||||
if (list == null)
|
||||
throw new IOException("list is null");
|
||||
else
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the destination of the symbolic link.
|
||||
*
|
||||
* @return file type
|
||||
* @throws UnsupportedOperationException if the file is not a symbolic link
|
||||
* @throws IOException on an IO error
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull String getLinkDestination() throws IOException {
|
||||
if (!isSymbolicLink())
|
||||
throw new UnsupportedOperationException("The file \"" + path + "\" is not a symbolic link");
|
||||
return unformatPath(Files.readSymbolicLink(path));
|
||||
}
|
||||
|
||||
// -----> Permissions
|
||||
/**
|
||||
* Returns whether or not the file can be read from.
|
||||
*
|
||||
* @return can be read?
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public boolean isReadable() {
|
||||
return Files.isReadable(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the file can be written to.
|
||||
*
|
||||
* @return can be written?
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public boolean isWritable() {
|
||||
return Files.isWritable(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the file can be executed.
|
||||
*
|
||||
* @return can be executed?
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public boolean isExecutable() {
|
||||
return Files.isExecutable(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file's permissions the
|
||||
* POSIX {@code rwxrwxrwx} format.
|
||||
*
|
||||
* @return POSIX permissions format
|
||||
* @throws IOException on an IO error
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull String getPosixPermissions() throws IOException {
|
||||
try {
|
||||
return PosixFilePermissions.toString(Files.getPosixFilePermissions(path));
|
||||
} catch (UnsupportedOperationException exception) {
|
||||
// POSIX permissions are not supported
|
||||
// For the Macrohard Windoze users under us
|
||||
StringBuilder output = new StringBuilder();
|
||||
|
||||
if (isReadable())
|
||||
output.append("r");
|
||||
if (isWritable())
|
||||
output.append("w");
|
||||
if (isExecutable())
|
||||
output.append("x");
|
||||
|
||||
// Repeat the same thing two times
|
||||
output.repeat(output, 2);
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file's permissions the
|
||||
* POSIX {@code rwxrwxrwx} format.
|
||||
*
|
||||
* @param permissions POSIX {@code rwxrwxrwx} permission format
|
||||
* @return this instance
|
||||
* @throws IllegalArgumentException if the format of the {@code permissions} argument is invalid
|
||||
* @throws IOException on an IO error
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
@SuppressWarnings({ "RegExpSingleCharAlternation", "ResultOfMethodCallIgnored" })
|
||||
public @NotNull FileAccess setPosixPermissions(@NotNull String permissions) throws IllegalArgumentException, IOException {
|
||||
if (
|
||||
permissions.length() != 9
|
||||
|| !permissions.matches("^(r|-)(w|-)(x|-)(r|-)(w|-)(x|-)(r|-)(w|-)(x|-)$")
|
||||
)
|
||||
throw new IllegalArgumentException("Invalid permission format: " + permissions);
|
||||
|
||||
try {
|
||||
Logger.diag("Setting POSIX file permissions for \"" + path + "\" to '" + permissions + "'");
|
||||
Files.setPosixFilePermissions(path, PosixFilePermissions.fromString(permissions));
|
||||
} catch (UnsupportedOperationException exception) {
|
||||
Logger.diag("Setting POSIX file permissions for \"" + path + "\" to '" + permissions.substring(0, 2) + "'");
|
||||
char @NotNull [] chars = permissions.toCharArray();
|
||||
|
||||
for (int permission = 0; permission < 3; permission++) {
|
||||
boolean enabled = chars[permission] != '-';
|
||||
|
||||
switch (permission) {
|
||||
case 0 -> file.setReadable(enabled);
|
||||
case 1 -> file.setWritable(enabled);
|
||||
case 2 -> file.setExecutable(enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// -----> Filesystem information
|
||||
/**
|
||||
* Returns the filesystem of this file.
|
||||
*
|
||||
* @return filesystem
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull FileSystem getFilesystem() {
|
||||
return path.getFileSystem();
|
||||
}
|
||||
/**
|
||||
* Returns whether or not the filesystem is POSIX-compliant.
|
||||
*
|
||||
* @return POSIX compliant?
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public boolean isFilesystemPosixCompliant() {
|
||||
return path.getFileSystem().supportedFileAttributeViews().contains("posix");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all forbidden file names.
|
||||
* <p>
|
||||
* The required functionality is not yet
|
||||
* implemented. As such, this method
|
||||
* will just return an empty array.
|
||||
*
|
||||
* @return forbidden file names
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public @NotNull String @NotNull [] getRestrictedFileNames() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all forbidden characters in file names.
|
||||
* <p>
|
||||
* The required functionality is not yet
|
||||
* implemented. As such, this method
|
||||
* will just return an empty array.
|
||||
*
|
||||
* @return forbidden characters
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
@ApiStatus.Experimental
|
||||
public char @NotNull [] getRestrictedCharacters() {
|
||||
return new char[0];
|
||||
}
|
||||
|
||||
|
||||
// -----> Directory traversal
|
||||
/**
|
||||
* Returns the parent directory.
|
||||
*
|
||||
* @return new {@link FileAccess} instance to the parent directory
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull FileAccess parent() {
|
||||
return new FileAccess(path.getParent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses through directories and files.
|
||||
*
|
||||
* @param path path to traverse
|
||||
* @return new {@link FileAccess} instance
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull FileAccess traverse(@NotNull String path) {
|
||||
return new FileAccess(this.path.resolve(formatPath(path)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses through directories and files.
|
||||
*
|
||||
* @param path path to traverse
|
||||
* @return new {@link FileAccess} instance
|
||||
* @throws FileNotFoundException if the specified path does not exist
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull FileAccess traverseIfExists(@NotNull String path) throws FileNotFoundException {
|
||||
Path pathResolved = this.path.resolve(formatPath(path));
|
||||
|
||||
if (!Files.exists(pathResolved))
|
||||
throw new FileNotFoundException("Traversal failed as relative path \"" + path + "\" does not exist from absolute path \"" + path + "\"");
|
||||
|
||||
return new FileAccess(pathResolved);
|
||||
}
|
||||
|
||||
|
||||
// -----> File/Directory creation and deletion
|
||||
/**
|
||||
* Deletes the file.
|
||||
* If it doesn't exist, nothing will be done.
|
||||
*
|
||||
* @return this instance
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull FileAccess delete() {
|
||||
if (exists()) {
|
||||
Logger.diag("Deleting \"" + path + "\"");
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
file.delete();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks this file for deletion at engine shutdown.
|
||||
*
|
||||
* @return this instance
|
||||
* @see Engine#shutdown()
|
||||
* @see Engine#shutdown(int)
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull FileAccess deleteOnShutdown() {
|
||||
Logger.diag("Marking \"" + path + "\" for deletion at engine shutdown");
|
||||
|
||||
// Append path to scheduledDeletion array
|
||||
List<@NotNull Path> scheduledDeletionList = new ArrayList<>(Arrays.stream(scheduledDeletion).toList());
|
||||
scheduledDeletionList.add(path);
|
||||
scheduledDeletion = scheduledDeletionList.toArray(new Path[0]);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the file.
|
||||
* If it already exists, nothing will be done.
|
||||
*
|
||||
* @return this instance
|
||||
* @throws IOException on an IO error
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
@SuppressWarnings({"UnusedReturnValue", "ResultOfMethodCallIgnored"})
|
||||
public @NotNull FileAccess createFile() throws IOException {
|
||||
if (!exists()) {
|
||||
Logger.diag("Creating a file at \"" + path + "\"");
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the directory recursively.
|
||||
* If it already exists, nothing will be done.
|
||||
*
|
||||
* @return this instance
|
||||
* @throws IOException on an IO error
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull FileAccess createDirectory() throws IOException {
|
||||
if (!exists()) {
|
||||
Logger.diag("Creating a directory at \"" + path + "\"");
|
||||
if (!file.mkdirs())
|
||||
throw new IOException("Creating directory \"" + path + "\" recursively failed");
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a symbolic link at this location.
|
||||
* If it already exists, nothing will be done.
|
||||
*
|
||||
* @param hard creates a hard link if {@code true} or a symbolic link if {@code false}
|
||||
* @param destination destination of the new link
|
||||
* @return this instance
|
||||
* @throws IOException on an IO error
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public @NotNull FileAccess createLink(boolean hard, @NotNull String destination) throws IOException {
|
||||
if (!exists()) {
|
||||
Logger.diag("Creating a " + (hard ? "hard" : "symbolic") + " link at \"" + path + "\"");
|
||||
|
||||
if (hard)
|
||||
Files.createLink(path, formatPath(destination));
|
||||
else
|
||||
Files.createSymbolicLink(path, formatPath(destination));
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// -----> File locking
|
||||
/**
|
||||
* Returns whether or not this file is locked.
|
||||
*
|
||||
* @return is locked?
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public boolean isLocked() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks this file.
|
||||
*
|
||||
* @return this instance
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull FileAccess lock() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks this file.
|
||||
*
|
||||
* @return this instance
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull FileAccess unlock() {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// -----> Content reading
|
||||
/**
|
||||
* Returns the contents of this file.
|
||||
* <p>
|
||||
* Returns an empty array if this file
|
||||
* is not of type {@link FileType#FILE}.
|
||||
*
|
||||
* @return file contents in bytes
|
||||
* @throws IOException on an IO error
|
||||
* @throws OutOfMemoryError if the file is larger than the allocated amount of memory
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public byte @NotNull [] readBytes() throws IOException, OutOfMemoryError {
|
||||
if (getType() != FileType.FILE)
|
||||
return new byte[0];
|
||||
|
||||
Logger.diag("Reading file \"" + path + "\" (bytes)");
|
||||
return Files.readAllBytes(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of this file.
|
||||
* <p>
|
||||
* Returns an empty list if this file
|
||||
* is not of type {@link FileType#FILE}.
|
||||
*
|
||||
* @return file contents in bytes
|
||||
* @throws IOException on an IO error
|
||||
* @throws OutOfMemoryError if the file is larger than the allocated amount of memory
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull List<@NotNull String> readLines() throws IOException, OutOfMemoryError {
|
||||
if (getType() != FileType.FILE)
|
||||
return new ArrayList<>();
|
||||
|
||||
Logger.diag("Reading file \"" + path + "\" (lines)");
|
||||
return Files.readAllLines(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of this file.
|
||||
* This method will decode the bytes using the
|
||||
* {@link StandardCharsets#UTF_8} character set.
|
||||
* <p>
|
||||
* Returns an empty string if this file
|
||||
* is not of type {@link FileType#FILE}.
|
||||
*
|
||||
* @return file contents as a string
|
||||
* @throws IOException on an IO error
|
||||
* @throws OutOfMemoryError if the file is larger than the allocated amount of memory
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull String readContent() throws IOException, OutOfMemoryError {
|
||||
return readContent(StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of this file.
|
||||
* <p>
|
||||
* Returns an empty string if this file
|
||||
* is not of type {@link FileType#FILE}.
|
||||
*
|
||||
* @param charset charset to decode the bytes with
|
||||
* @return file contents as a string
|
||||
* @throws IOException on an IO error
|
||||
* @throws OutOfMemoryError if the file is larger than the allocated amount of memory
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
public @NotNull String readContent(@NotNull Charset charset) throws IOException, OutOfMemoryError {
|
||||
if (getType() != FileType.FILE)
|
||||
return "";
|
||||
|
||||
Logger.diag("Reading file \"" + path + "\" (string)");
|
||||
return Files.readString(path, charset);
|
||||
}
|
||||
|
||||
|
||||
// -----> Content writing
|
||||
/**
|
||||
* Writes the specified bytes into this file.
|
||||
*
|
||||
* @param bytes bytes to write
|
||||
* @param async allows the operating system to decide when to flush the file to disk if {@code true}, flushes the data to disk immediately if {@code false}
|
||||
* @throws UnsupportedOperationException if the type of this file is neither {@link FileType#VOID} or {@link FileType#FILE}
|
||||
* @throws IOException on an IO error
|
||||
* @return this instance
|
||||
*/
|
||||
public @NotNull FileAccess writeBytes(byte @NotNull [] bytes, boolean async) throws UnsupportedOperationException, IOException {
|
||||
if (getType() == FileType.VOID)
|
||||
createFile();
|
||||
else if (getType() != FileType.FILE)
|
||||
throw new UnsupportedOperationException("File \"" + path + "\" is not of type FileType.VOID or FileType.FILE");
|
||||
|
||||
createFile();
|
||||
Logger.diag("Writing file \"" + path + "\" (bytes, " + (async ? "async" : "dsync") + ")");
|
||||
Files.write(path, bytes, StandardOpenOption.WRITE, async ? StandardOpenOption.DSYNC : StandardOpenOption.SYNC);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified bytes into this file.
|
||||
* This method will encode the string using the
|
||||
* {@link StandardCharsets#UTF_8} character set.
|
||||
*
|
||||
* @param string string to write
|
||||
* @param async allows the operating system to decide when to flush the file to disk if {@code true}, flushes the data to disk immediately if {@code false}
|
||||
* @throws UnsupportedOperationException if the type of this file is neither {@link FileType#VOID} or {@link FileType#FILE}
|
||||
* @throws IOException on an IO error
|
||||
* @return this instance
|
||||
*/
|
||||
public @NotNull FileAccess writeString(@NotNull String string, boolean async) throws UnsupportedOperationException, IOException {
|
||||
return writeString(string, StandardCharsets.UTF_8, async);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified bytes into this file.
|
||||
*
|
||||
* @param string string to write
|
||||
* @param charset charset to encode the string in
|
||||
* @param async allows the operating system to decide when to flush the file to disk if {@code true}, flushes the data to disk immediately if {@code false}
|
||||
* @throws UnsupportedOperationException if the type of this file is neither {@link FileType#VOID} or {@link FileType#FILE}
|
||||
* @throws IOException on an IO error
|
||||
* @return this instance
|
||||
*/
|
||||
public @NotNull FileAccess writeString(@NotNull String string, @NotNull Charset charset, boolean async) throws UnsupportedOperationException, IOException {
|
||||
if (getType() == FileType.VOID)
|
||||
createFile();
|
||||
else if (getType() != FileType.FILE)
|
||||
throw new UnsupportedOperationException("File \"" + path + "\" is not of type FileType.VOID or FileType.FILE");
|
||||
|
||||
Logger.diag("Writing file \"" + path + "\" (string, " + (async ? "async" : "dsync") + ")");
|
||||
Files.writeString(path, string, charset, StandardOpenOption.WRITE, async ? StandardOpenOption.DSYNC : StandardOpenOption.SYNC);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
// -----> Content appending
|
||||
/**
|
||||
* Appends the specified bytes to this file.
|
||||
*
|
||||
* @param bytes bytes to append
|
||||
* @param async allows the operating system to decide when to flush the file to disk if {@code true}, flushes the data to disk immediately if {@code false}
|
||||
* @throws UnsupportedOperationException if the type of this file is neither {@link FileType#VOID} or {@link FileType#FILE}
|
||||
* @throws IOException on an IO error
|
||||
* @return this instance
|
||||
*/
|
||||
public @NotNull FileAccess appendBytes(byte @NotNull [] bytes, boolean async) throws UnsupportedOperationException, IOException {
|
||||
if (getType() == FileType.VOID)
|
||||
createFile();
|
||||
else if (getType() != FileType.FILE)
|
||||
throw new UnsupportedOperationException("File \"" + path + "\" is not of type FileType.VOID or FileType.FILE");
|
||||
|
||||
Logger.diag("Appending file \"" + path + "\" (bytes, " + (async ? "async" : "dsync") + ")");
|
||||
Files.write(path, bytes, StandardOpenOption.APPEND, async ? StandardOpenOption.DSYNC : StandardOpenOption.SYNC);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the specified string to this file.
|
||||
* This method will encode the string using the
|
||||
* {@link StandardCharsets#UTF_8} character set.
|
||||
*
|
||||
* @param string string to append
|
||||
* @param async allows the operating system to decide when to flush the file to disk if {@code true}, flushes the data to disk immediately if {@code false}
|
||||
* @throws UnsupportedOperationException if the type of this file is neither {@link FileType#VOID} or {@link FileType#FILE}
|
||||
* @throws IOException on an IO error
|
||||
* @return this instance
|
||||
*/
|
||||
public @NotNull FileAccess appendString(@NotNull String string, boolean async) throws UnsupportedOperationException, IOException {
|
||||
return appendString(string, StandardCharsets.UTF_8, async);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the specified string to this file.
|
||||
*
|
||||
* @param string string to append
|
||||
* @param charset charset to encode the string in
|
||||
* @param async allows the operating system to decide when to flush the file to disk if {@code true}, flushes the data to disk immediately if {@code false}
|
||||
* @throws UnsupportedOperationException if the type of this file is neither {@link FileType#VOID} or {@link FileType#FILE}
|
||||
* @throws IOException on an IO error
|
||||
* @return this instance
|
||||
*/
|
||||
public @NotNull FileAccess appendString(@NotNull String string, @NotNull Charset charset, boolean async) throws UnsupportedOperationException, IOException {
|
||||
if (getType() == FileType.VOID)
|
||||
createFile();
|
||||
else if (getType() != FileType.FILE)
|
||||
throw new UnsupportedOperationException("File \"" + path + "\" is not of type FileType.VOID or FileType.FILE");
|
||||
|
||||
Logger.diag("Appending file \"" + path + "\" (string, " + (async ? "async" : "dsync") + ")");
|
||||
Files.writeString(path, string, charset, StandardOpenOption.APPEND, async ? StandardOpenOption.DSYNC : StandardOpenOption.SYNC);
|
||||
return this;
|
||||
}
|
||||
}
|
|
@ -22,6 +22,7 @@ package de.staropensource.engine.base.utility;
|
|||
import de.staropensource.engine.base.Engine;
|
||||
import de.staropensource.engine.base.implementable.Placeholder;
|
||||
import de.staropensource.engine.base.internal.implementation.placeholder.*;
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -49,6 +50,14 @@ public final class PlaceholderEngine {
|
|||
@Getter
|
||||
private static PlaceholderEngine instance;
|
||||
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
private final @NotNull LoggerInstance logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("ENGINE").build();
|
||||
|
||||
/**
|
||||
* Contains all global placeholders.
|
||||
*
|
||||
|
@ -97,7 +106,6 @@ public final class PlaceholderEngine {
|
|||
placeholders.add(new EngineGitDirty());
|
||||
// engine_version*
|
||||
placeholders.add(new EngineVersion());
|
||||
placeholders.add(new EngineVersionCodename());
|
||||
placeholders.add(new EngineVersionVersion());
|
||||
placeholders.add(new EngineVersionType());
|
||||
placeholders.add(new EngineVersionTyperelease());
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
package de.staropensource.engine.base.utility;
|
||||
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import de.staropensource.engine.base.type.Tristate;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -49,6 +49,14 @@ public final class PropertiesReader {
|
|||
@Getter
|
||||
private static final @NotNull PropertiesReader instance = new PropertiesReader(System.getProperties());
|
||||
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private final @NotNull LoggerInstance logger;
|
||||
|
||||
/**
|
||||
* Contains the {@link Properties} used by this parser instance to read properties.
|
||||
*
|
||||
|
@ -72,6 +80,7 @@ public final class PropertiesReader {
|
|||
*/
|
||||
public PropertiesReader(@NotNull Properties properties) {
|
||||
this.properties = properties;
|
||||
this.logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("ENGINE").setMetadata(String.valueOf(properties.hashCode())).build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -85,7 +94,7 @@ public final class PropertiesReader {
|
|||
*/
|
||||
public @NotNull String getString(@NotNull String name) throws NullPointerException {
|
||||
if (properties.getProperty(name) == null) {
|
||||
Logger.sarn("Unable to get String from property '" + name + "': Property does not exist");
|
||||
logger.sarn("Unable to get String from property '" + name + "': Property does not exist");
|
||||
throw new NullPointerException("Unable to get String from property '" + name + "': Property does not exist");
|
||||
}
|
||||
return properties.getProperty(name);
|
||||
|
@ -102,7 +111,7 @@ public final class PropertiesReader {
|
|||
*/
|
||||
public boolean getBoolean(@NotNull String name) throws NullPointerException {
|
||||
if (properties.getProperty(name) == null) {
|
||||
Logger.sarn("Unable to get Boolean from property '" + name + "': Property does not exist");
|
||||
logger.sarn("Unable to get Boolean from property '" + name + "': Property does not exist");
|
||||
throw new NullPointerException("Unable to get Boolean from property '" + name + "': Property does not exist");
|
||||
}
|
||||
|
||||
|
@ -127,7 +136,7 @@ public final class PropertiesReader {
|
|||
*/
|
||||
public @NotNull Tristate getTristate(@NotNull String name) throws NullPointerException {
|
||||
if (properties.getProperty(name) == null) {
|
||||
Logger.sarn("Unable to get Tristate from property '" + name + "': Property does not exist");
|
||||
logger.sarn("Unable to get Tristate from property '" + name + "': Property does not exist");
|
||||
throw new NullPointerException("Unable to get Tristate from property '" + name + "': Property does not exist");
|
||||
}
|
||||
|
||||
|
@ -156,14 +165,14 @@ public final class PropertiesReader {
|
|||
*/
|
||||
public byte getByte(@NotNull String name) throws NullPointerException, NumberFormatException {
|
||||
if (properties.getProperty(name) == null) {
|
||||
Logger.sarn("Unable to get Byte from property '" + name + "': Property does not exist");
|
||||
logger.sarn("Unable to get Byte from property '" + name + "': Property does not exist");
|
||||
throw new NullPointerException("Unable to get Byte from property '" + name + "': Property does not exist");
|
||||
}
|
||||
|
||||
try {
|
||||
return Byte.parseByte(properties.getProperty(name));
|
||||
} catch (NumberFormatException exception) {
|
||||
Logger.sarn("Unable to get Byte from property '" + name + "': String cannot be parsed as a Byte.");
|
||||
logger.sarn("Unable to get Byte from property '" + name + "': String cannot be parsed as a Byte.");
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
@ -180,14 +189,14 @@ public final class PropertiesReader {
|
|||
*/
|
||||
public short getShort(@NotNull String name) throws NullPointerException, NumberFormatException {
|
||||
if (properties.getProperty(name) == null) {
|
||||
Logger.sarn("Unable to get Short from property '" + name + "': Property does not exist");
|
||||
logger.sarn("Unable to get Short from property '" + name + "': Property does not exist");
|
||||
throw new NullPointerException("Unable to get Short from property '" + name + "': Property does not exist");
|
||||
}
|
||||
|
||||
try {
|
||||
return Short.parseShort(properties.getProperty(name));
|
||||
} catch (NumberFormatException exception) {
|
||||
Logger.sarn("Unable to get Short from property '" + name + "': String cannot be parsed as a Short.");
|
||||
logger.sarn("Unable to get Short from property '" + name + "': String cannot be parsed as a Short.");
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
@ -205,7 +214,7 @@ public final class PropertiesReader {
|
|||
*/
|
||||
public int getInteger(@NotNull String name, boolean unsigned) throws NullPointerException, NumberFormatException {
|
||||
if (properties.getProperty(name) == null) {
|
||||
Logger.sarn("Unable to get Integer from property '" + name + "': Property does not exist");
|
||||
logger.sarn("Unable to get Integer from property '" + name + "': Property does not exist");
|
||||
throw new NullPointerException("Unable to get Integer from property '" + name + "': Property does not exist");
|
||||
}
|
||||
|
||||
|
@ -215,7 +224,7 @@ public final class PropertiesReader {
|
|||
else
|
||||
return Integer.parseInt(properties.getProperty(name));
|
||||
} catch (NumberFormatException exception) {
|
||||
Logger.sarn("Unable to get Integer from property '" + name + "': String cannot be parsed as an Integer.");
|
||||
logger.sarn("Unable to get Integer from property '" + name + "': String cannot be parsed as an Integer.");
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
@ -233,7 +242,7 @@ public final class PropertiesReader {
|
|||
*/
|
||||
public long getLong(@NotNull String name, boolean unsigned) throws NullPointerException, NumberFormatException {
|
||||
if (properties.getProperty(name) == null) {
|
||||
Logger.sarn("Unable to get Long from property '" + name + "': Property does not exist");
|
||||
logger.sarn("Unable to get Long from property '" + name + "': Property does not exist");
|
||||
throw new NullPointerException("Unable to get Long from property '" + name + "': Property does not exist");
|
||||
}
|
||||
|
||||
|
@ -243,7 +252,7 @@ public final class PropertiesReader {
|
|||
else
|
||||
return Long.parseLong(properties.getProperty(name));
|
||||
} catch (NumberFormatException exception) {
|
||||
Logger.sarn("Unable to get Long from property '" + name + "': String cannot be parsed as a Long.");
|
||||
logger.sarn("Unable to get Long from property '" + name + "': String cannot be parsed as a Long.");
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
@ -260,14 +269,14 @@ public final class PropertiesReader {
|
|||
*/
|
||||
public float getFloat(@NotNull String name) throws NullPointerException, NumberFormatException {
|
||||
if (properties.getProperty(name) == null) {
|
||||
Logger.sarn("Unable to get Float from property '" + name + "': Property does not exist");
|
||||
logger.sarn("Unable to get Float from property '" + name + "': Property does not exist");
|
||||
throw new NullPointerException("Unable to get Float from property '" + name + "': Property does not exist");
|
||||
}
|
||||
|
||||
try {
|
||||
return Float.parseFloat(properties.getProperty(name));
|
||||
} catch (NumberFormatException exception) {
|
||||
Logger.sarn("Unable to get Float from property '" + name + "': String cannot be parsed as a Float.");
|
||||
logger.sarn("Unable to get Float from property '" + name + "': String cannot be parsed as a Float.");
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
@ -284,17 +293,17 @@ public final class PropertiesReader {
|
|||
*/
|
||||
public double getDouble(@NotNull String name) throws NullPointerException, NumberFormatException {
|
||||
if (properties.getProperty(name) == null) {
|
||||
Logger.sarn("Unable to get Double from property '" + name + "': Property does not exist");
|
||||
logger.sarn("Unable to get Double from property '" + name + "': Property does not exist");
|
||||
throw new NullPointerException("Unable to get Double from property '" + name + "': Property does not exist");
|
||||
}
|
||||
|
||||
try {
|
||||
return Double.parseDouble(properties.getProperty(name));
|
||||
} catch (NullPointerException exception) {
|
||||
Logger.sarn("Unable to get Double from property '" + name + "': String is null.");
|
||||
logger.sarn("Unable to get Double from property '" + name + "': String is null.");
|
||||
throw exception;
|
||||
} catch (NumberFormatException exception) {
|
||||
Logger.sarn("Unable to get Double from property '" + name + "': String cannot be parsed as a Double.");
|
||||
logger.sarn("Unable to get Double from property '" + name + "': String cannot be parsed as a Double.");
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,13 @@
|
|||
|
||||
package de.staropensource.engine.base.utility.information;
|
||||
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.Engine;
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import de.staropensource.engine.base.type.VersionType;
|
||||
import de.staropensource.engine.base.utility.Miscellaneous;
|
||||
import de.staropensource.engine.base.utility.PropertiesReader;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
@ -41,6 +44,14 @@ import java.util.Properties;
|
|||
*/
|
||||
@SuppressWarnings({ "JavadocDeclaration" })
|
||||
public final class EngineInformation {
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private static final @NotNull LoggerInstance logger = new LoggerInstance.Builder().setClazz(EngineInformation.class).setOrigin("ENGINE").build();
|
||||
|
||||
/**
|
||||
* Contains the engine's version codename.
|
||||
*
|
||||
|
@ -337,14 +348,14 @@ public final class EngineInformation {
|
|||
* @since v1-alpha1
|
||||
*/
|
||||
public static synchronized void update() {
|
||||
Logger.diag("Updating engine information");
|
||||
logger.diag("Updating engine information");
|
||||
|
||||
// Load properties from bundled gradle.properties
|
||||
Properties gradleProperties = new Properties();
|
||||
InputStream gradleStream = EngineInformation.class.getClassLoader().getResourceAsStream("sosengine-gradle.properties");
|
||||
|
||||
if (gradleStream == null) {
|
||||
Logger.crash("Unable to load build information: The bundled gradle.properties file could not be found.");
|
||||
logger.crash("Unable to load build information: The bundled gradle.properties file could not be found.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -352,7 +363,7 @@ public final class EngineInformation {
|
|||
gradleProperties.load(gradleStream);
|
||||
gradleStream.close();
|
||||
} catch (IOException exception) {
|
||||
Logger.crash("Unable to load build information: InputStream 'gradleStream' failed", exception);
|
||||
logger.crash("Unable to load build information: InputStream 'gradleStream' failed", exception);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -361,7 +372,7 @@ public final class EngineInformation {
|
|||
Properties gitProperties = new Properties();
|
||||
InputStream gitStream = EngineInformation.class.getClassLoader().getResourceAsStream("sosengine-git.properties");
|
||||
if (gitStream == null) {
|
||||
Logger.error("Unable to load build information: The bundled git.properties file could not be found. Did you download a tarball?");
|
||||
logger.error("Unable to load build information: The bundled git.properties file could not be found. Did you download a tarball?");
|
||||
|
||||
// Fake information
|
||||
gitProperties.setProperty("git.total.commit.count", "0");
|
||||
|
@ -379,7 +390,7 @@ public final class EngineInformation {
|
|||
gitProperties.load(gitStream);
|
||||
gitStream.close();
|
||||
} catch (IOException exception) {
|
||||
Logger.crash("Unable to load build information: InputStream 'gitStream' failed", exception);
|
||||
logger.crash("Unable to load build information: InputStream 'gitStream' failed", exception);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -416,7 +427,9 @@ public final class EngineInformation {
|
|||
calendar.setTime(date);
|
||||
gitCommitTime = calendar.toZonedDateTime();
|
||||
} catch (ParseException exception) {
|
||||
Logger.crash("Unable to load build information: Can't parse \"" + gitParser.getString("git.commit.time") + "\" using format \"yyyy-MM-dd'T'HH:mmZ\"", exception);
|
||||
System.out.println("Unable to load build information: Can't parse \"" + gitParser.getString("git.commit.time") + "\" using format \"yyyy-MM-dd'T'HH:mmZ\"");
|
||||
System.out.println(Miscellaneous.getStackTraceHeader(exception) + "\n" + Miscellaneous.getStackTraceAsString(exception, true));
|
||||
Engine.getInstance().shutdown(69);
|
||||
return;
|
||||
}
|
||||
gitCommitterName = gitParser.getString("git.commit.user.name");
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//package de.staropensource.engine.base.utility.information;
|
||||
|
||||
/**
|
||||
* Provides information about the environment
|
||||
* ie. the operating system, paths, etc..
|
||||
*
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
//public final class EnvironmentInformation {
|
||||
//}
|
|
@ -19,8 +19,7 @@
|
|||
|
||||
package de.staropensource.engine.base.utility.information;
|
||||
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.type.immutable.ImmutableArrayList;
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
|
@ -34,6 +33,14 @@ import java.util.List;
|
|||
*/
|
||||
@SuppressWarnings({ "unused" })
|
||||
public final class JvmInformation {
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private static final @NotNull LoggerInstance logger = new LoggerInstance.Builder().setClazz(JvmInformation.class).setOrigin("ENGINE").build();
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
|
@ -61,7 +68,7 @@ public final class JvmInformation {
|
|||
try {
|
||||
return Integer.parseInt(version);
|
||||
} catch (NumberFormatException exception) {
|
||||
Logger.crash("Could not parse Java version: Integer conversion failed for string \"" + version + "\"", exception, true);
|
||||
logger.crash("Could not parse Java version: Integer conversion failed for string \"" + version + "\"", exception, true);
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
@ -110,11 +117,11 @@ public final class JvmInformation {
|
|||
* Returns all arguments passed to the JVM.
|
||||
* This excludes all arguments passed to the application.
|
||||
*
|
||||
* @return immutable list with all JVM arguments
|
||||
* @return JVM arguments
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public static @NotNull List<@NotNull String> getArguments() {
|
||||
return new ImmutableArrayList<>(ManagementFactory.getRuntimeMXBean().getInputArguments());
|
||||
return ManagementFactory.getRuntimeMXBean().getInputArguments();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,13 +18,13 @@ module sosengine.base {
|
|||
exports de.staropensource.engine.base.implementable;
|
||||
exports de.staropensource.engine.base.implementable.helper;
|
||||
exports de.staropensource.engine.base.utility.information;
|
||||
exports de.staropensource.engine.base.implementation.shortcode;
|
||||
exports de.staropensource.engine.base.implementation.versioning;
|
||||
exports de.staropensource.engine.base.event;
|
||||
exports de.staropensource.engine.base.exception;
|
||||
exports de.staropensource.engine.base.exception.dependency;
|
||||
exports de.staropensource.engine.base.exception.reflection;
|
||||
exports de.staropensource.engine.base.exception.versioning;
|
||||
exports de.staropensource.engine.base.internal.event; // Internal: Required for subsystems
|
||||
exports de.staropensource.engine.base.logging;
|
||||
exports de.staropensource.engine.base.implementation.logging;
|
||||
exports de.staropensource.engine.base.reflection;
|
||||
|
@ -34,6 +34,7 @@ module sosengine.base {
|
|||
exports de.staropensource.engine.base.type.reflection;
|
||||
exports de.staropensource.engine.base.type.vector;
|
||||
exports de.staropensource.engine.base.utility;
|
||||
exports de.staropensource.engine.base.implementation.shortcode;
|
||||
|
||||
// Reflection access
|
||||
opens de.staropensource.engine.base;
|
||||
|
@ -41,13 +42,13 @@ module sosengine.base {
|
|||
opens de.staropensource.engine.base.implementable;
|
||||
opens de.staropensource.engine.base.implementable.helper;
|
||||
opens de.staropensource.engine.base.utility.information;
|
||||
opens de.staropensource.engine.base.implementation.shortcode;
|
||||
opens de.staropensource.engine.base.implementation.versioning;
|
||||
opens de.staropensource.engine.base.event;
|
||||
opens de.staropensource.engine.base.exception;
|
||||
opens de.staropensource.engine.base.exception.dependency;
|
||||
opens de.staropensource.engine.base.exception.reflection;
|
||||
opens de.staropensource.engine.base.exception.versioning;
|
||||
opens de.staropensource.engine.base.internal.event; // Internal: Required for subsystems
|
||||
opens de.staropensource.engine.base.logging;
|
||||
opens de.staropensource.engine.base.implementation.logging;
|
||||
opens de.staropensource.engine.base.reflection;
|
||||
|
@ -57,4 +58,5 @@ module sosengine.base {
|
|||
opens de.staropensource.engine.base.type.reflection;
|
||||
opens de.staropensource.engine.base.type.vector;
|
||||
opens de.staropensource.engine.base.utility;
|
||||
opens de.staropensource.engine.base.implementation.shortcode;
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ import org.junit.jupiter.api.Test;
|
|||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
|
@ -54,10 +53,11 @@ class EngineConfigurationTest extends TestBase {
|
|||
settings.put("debug", new Object[]{ "true", Boolean.TRUE });
|
||||
settings.put("debugEvents", new Object[]{ "true", Boolean.TRUE });
|
||||
settings.put("errorShortcodeParser", new Object[]{ "false", Boolean.FALSE });
|
||||
settings.put("logLevel", new Object[]{ "verbose", LogLevel.VERBOSE });
|
||||
settings.put("logFeatures", new Object[]{ "formatting,runtime,time", Set.of("formatting", "runtime", "time") });
|
||||
settings.put("logForceStandardOutput", new Object[]{ "true", Boolean.TRUE });
|
||||
settings.put("logPollingSpeed", new Object[]{ "9999", 9999 });
|
||||
settings.put("loggerLevel", new Object[]{ "verbose", LogLevel.VERBOSE });
|
||||
settings.put("loggerTemplate", new Object[]{ "%log_path% says: %message%", "%log_path% says: %message%" });
|
||||
settings.put("loggerImmediateShutdown", new Object[]{ "true", Boolean.TRUE });
|
||||
settings.put("loggerForceStandardOutput", new Object[]{ "true", Boolean.TRUE });
|
||||
settings.put("loggerPollingSpeed", new Object[]{ "9999", 9999 });
|
||||
settings.put("optimizeLogging", new Object[]{ "false", Boolean.FALSE });
|
||||
settings.put("optimizeEvents", new Object[]{ "false", Boolean.FALSE });
|
||||
|
||||
|
|
|
@ -22,7 +22,6 @@ package de.staropensource.engine.base.srctests.utility;
|
|||
import de.staropensource.engine.base.EngineConfiguration;
|
||||
import de.staropensource.engine.base.annotation.EventListener;
|
||||
import de.staropensource.engine.base.event.ThrowableCatchEvent;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.testing.TestBase;
|
||||
import de.staropensource.engine.base.utility.Math;
|
||||
import de.staropensource.engine.base.utility.Miscellaneous;
|
||||
|
@ -126,7 +125,7 @@ public class MiscellaneousTest extends TestBase {
|
|||
return;
|
||||
|
||||
throwableCaught = false;
|
||||
Miscellaneous.executeSafely(() -> Logger.info("You can safely ignore this message (this comes from MiscellaneousTest#testExecuteSafely0)"), "MiscellaneousTest#testExecuteSafely0");
|
||||
Miscellaneous.executeSafely(() -> System.out.println("You can safely ignore this message (this comes from MiscellaneousTest#testExecuteSafely0)"), "MiscellaneousTest#testExecuteSafely0");
|
||||
|
||||
assertFalse(throwableCaught, "Event was triggered");
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@ tasks.register("javadocAll", Javadoc) {
|
|||
":base",
|
||||
":ansi",
|
||||
":slf4j-compat",
|
||||
":rendering",
|
||||
":windowing",
|
||||
":windowing:glfw",
|
||||
":notification",
|
||||
]
|
||||
|
||||
|
@ -62,7 +63,7 @@ tasks.register("javadocAll", Javadoc) {
|
|||
|
||||
// Fix module collisions
|
||||
doFirst {
|
||||
getLogger().log(LogLevel.WARN, "If this task fails, make sure to reset all module-info.java files using git or you may encounter issues.")
|
||||
logger.log(LogLevel.WARN, "If this task fails, make sure to reset all module-info.java files using git or you may encounter issues.")
|
||||
|
||||
for (String subproject : subprojects) {
|
||||
File source = new File(project(subproject).projectDir.getPath() + "/src/main/java/module-info.java")
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
"position": 2,
|
||||
"link": {
|
||||
"type": "generated-index",
|
||||
"description": "Provides a complete guide on how to get started with the StarOpenSource Engine."
|
||||
"description": "Contains pages explaining how to setup the sos!engine in your project."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,20 +8,8 @@ To initialize the sos!engine, simply add this to the initialization code of your
|
|||
```java
|
||||
Engine.initialize();
|
||||
```
|
||||
... or if you want error handling (recommended):
|
||||
```java
|
||||
try {
|
||||
Engine.initialize();
|
||||
} catch (Exception exception) {
|
||||
// Make sure this kills your
|
||||
// game or application. This
|
||||
// example is assuming that this
|
||||
// code lives inside a Main class.
|
||||
return;
|
||||
}
|
||||
```
|
||||
|
||||
This is enough to initialize the core engine and all subsystems found in the classpath. No need to manually initialize them.
|
||||
This is enough to initialize the engine and all installed subsystems. No need to manually initialize them.
|
||||
|
||||
## Printing something
|
||||
Now you'll probably want to print some log output. Before you try using `System.out#println`,
|
||||
|
@ -31,43 +19,104 @@ provides it's own logging implementation and is HIGHLY recommended to be used ov
|
|||
There are eight log levels you can use:
|
||||
<table>
|
||||
<tr>
|
||||
<th>LEVEL</th>
|
||||
<th>METHOD NAME</th>
|
||||
<th>DESCRIPTION</th>
|
||||
<th>Level</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>`DIAGNOSTIC`</th>
|
||||
<th>`diag`</th>
|
||||
<th>Detailed information about what is happening</th>
|
||||
<th>DIAGNOSTIC</th>
|
||||
<th>Provide detailed information about what is happening</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>`VERBOSE`</th>
|
||||
<th>`verb`</th>
|
||||
<th>Additional information about what is happening</th>
|
||||
<th>VERBOSE</th>
|
||||
<th>Additional information that may not be useful</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>`SILENT_WARNING`</th>
|
||||
<th>`sarn`</th>
|
||||
<th>Less important warnings. Useful for logging parsing errors and such</th>
|
||||
<th>SILENT_WARNING</th>
|
||||
<th>Warnings that can be ignored or are caused by invalid (API) user input</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>`INFORMATIONAL`</th>
|
||||
<th>`info`</th>
|
||||
<th>INFORMATIONAL</th>
|
||||
<th>Useful information about what is happening</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>`WARNING`</th>
|
||||
<th>`warn`</th>
|
||||
<th>Warnings about dangerous, deprecated or weird behaviour</th>
|
||||
<th>WARNING</th>
|
||||
<th>Warnings about dangerous or weird behaviour</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>`ERROR`</th>
|
||||
<th>`error`</th>
|
||||
<th>ERROR</th>
|
||||
<th>Non-fatal errors</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>`CRASH`</th>
|
||||
<th>`crash`</th>
|
||||
<th>CRASH</th>
|
||||
<th>Fatal errors which may or may not halt the running program (see below)</th>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Here's an example:
|
||||
```java
|
||||
Logger.info(getClass(), "APPLICATION", null, "Hello World!");
|
||||
// ^ ^ ^ ^
|
||||
// | | | |
|
||||
// | | | --- The message you want to print.
|
||||
// | | -------- Metadata string (can be null). If your class/method processes data or something, you can include it here.
|
||||
// | ---------------------- Origin of the message. It identifies from which part of your application the message came.
|
||||
// -------------------------------------- Log priority/type. In this case, we want to emit an informational message
|
||||
```
|
||||
|
||||
Now, why do I need to supply so many arguments just for a simple "Hello World!"?
|
||||
It's simple: It describes the class issuing the log call.
|
||||
|
||||
Here's an example:
|
||||
```java
|
||||
class ExampleClass() {
|
||||
private @NotNull String string;
|
||||
|
||||
public ExampleClass(@NotNull String string) {
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
public boolean checkForEscapes() {
|
||||
Logger.diag(getClass(), "APPLICATION", string, "Checking for escapes");
|
||||
return string.contains("\\");
|
||||
}
|
||||
|
||||
public boolean checkIfEmpty() {
|
||||
Logger.diag(getClass(), "APPLICATION", string, "Checking if empty");
|
||||
return string.isEmpty();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Now that's nice and all, but it's annoying to pass three arguments describing your class to log methods
|
||||
and will – over time – make log messages unorganized and unmaintainable. That's not good. That's why
|
||||
the `LoggerInstance` class exists. It's purpose is to pass these to every log call you make. The only
|
||||
thing you need to do it to is to pass it some data about your class and it does the job for you.
|
||||
|
||||
Here's an example of the code before, but with `LoggerInstance`:
|
||||
```java
|
||||
class ExampleClass() {
|
||||
private @NotNull LoggerInstance logger;
|
||||
private @NotNull String string;
|
||||
|
||||
public ExampleClass(@NotNull String string) {
|
||||
this.string = string;
|
||||
logger = LoggerInstance.Builder()
|
||||
.setClass(getClass())
|
||||
.setOrigin("APPLICATION") // If you intend on 'origin' being set to "APPLICATION", you can leave this out. This has just been included for completeness.
|
||||
.setMetadata(string) // Include the string we are doing stuff with as metadata
|
||||
.build();
|
||||
}
|
||||
|
||||
public boolean checkForEscapes() {
|
||||
logger.diag("Checking for escapes");
|
||||
return string.contains("\\");
|
||||
}
|
||||
|
||||
public boolean checkIfEmpty() {
|
||||
logger.diag("Checking if empty");
|
||||
return string.isEmpty();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
As you can see, it's much simpler. No need to pass that to every call now!
|
||||
|
|
|
@ -80,7 +80,7 @@ dependencies {
|
|||
... and add this property to the `settings.gradle` file:
|
||||
```properties
|
||||
# Set this to the engine version you want to use
|
||||
dependencyStarOpenSourceEngine=1-alpha8
|
||||
dependencyStarOpenSourceEngine=1-alpha6
|
||||
```
|
||||
|
||||
## Maven
|
||||
|
@ -99,12 +99,13 @@ Add StarOpenSource's maven repository to your `pom.xml` file first:
|
|||
After that declare the engine as a dependency in your project:
|
||||
|
||||
```xml
|
||||
|
||||
<dependencies>
|
||||
<!-- sos!engine base -->
|
||||
<dependency>
|
||||
<groupId>de.staropensource.engine</groupId>
|
||||
<artifactId>base</artifactId>
|
||||
<version>1-alpha8</version>
|
||||
<version>1-alpha6</version>
|
||||
</dependency>
|
||||
|
||||
<!-- sos!engine subsystems -->
|
||||
|
@ -112,35 +113,35 @@ After that declare the engine as a dependency in your project:
|
|||
<dependency>
|
||||
<groupId>de.staropensource.engine</groupId>
|
||||
<artifactId>ansi</artifactId>
|
||||
<version>1-alpha8</version>
|
||||
<version>v1-alpha6</version>
|
||||
</dependency>
|
||||
-->
|
||||
<!-- SLF4J compatibility
|
||||
<dependency>
|
||||
<groupId>de.staropensource.engine</groupId>
|
||||
<artifactId>slf4j-compat</artifactId>
|
||||
<version>1-alpha8</version>
|
||||
<version>1-alpha6</version>
|
||||
</dependency>
|
||||
-->
|
||||
<!-- creating and managing windows, requires a Windowing API implementation
|
||||
<dependency>
|
||||
<groupId>de.staropensource.engine</groupId>
|
||||
<artifactId>windowing</artifactId>
|
||||
<version>1-alpha8</version>
|
||||
<version>v1-alpha6</version>
|
||||
</dependency>
|
||||
-->
|
||||
<!-- Windowing API implementation using GLFW
|
||||
<dependency>
|
||||
<groupId>de.staropensource.engine</groupId>
|
||||
<artifactId>glfw</artifactId>
|
||||
<version>1-alpha8</version>
|
||||
<version>v1-alpha6</version>
|
||||
</dependency>
|
||||
-->
|
||||
<!-- sending and receiving notifications inside your application
|
||||
<dependency>
|
||||
<groupId>de.staropensource.engine</groupId>
|
||||
<artifactId>notification</artifactId>
|
||||
<version>1-alpha8</version>
|
||||
<version>v1-alpha6</version>
|
||||
</dependency>
|
||||
-->
|
||||
</dependencies>
|
||||
|
|
|
@ -67,21 +67,3 @@ The engine API documentation covers the core engine and all official subsystems.
|
|||
- [windowing](https://jd.engine.staropensource.de/v1-alpha6/windowing/)
|
||||
- [windowing:glfw](https://jd.engine.staropensource.de/v1-alpha6/windowing:glfw/)
|
||||
- [notification](https://jd.engine.staropensource.de/v1-alpha6/notification/)
|
||||
- v1-alpha7
|
||||
- [All subsystem](https://jd.engine.staropensource.de/v1-alpha7/all/)
|
||||
- [base](https://jd.engine.staropensource.de/v1-alpha7/base/)
|
||||
- [testing](https://jd.engine.staropensource.de/v1-alpha7/testing/)
|
||||
- [ansi](https://jd.engine.staropensource.de/v1-alpha7/ansi/)
|
||||
- [slf4j-compat](https://jd.engine.staropensource.de/v1-alpha7/slf4j-compat/)
|
||||
- [windowing](https://jd.engine.staropensource.de/v1-alpha7/windowing/)
|
||||
- [windowing:glfw](https://jd.engine.staropensource.de/v1-alpha7/windowing:glfw/)
|
||||
- [notification](https://jd.engine.staropensource.de/v1-alpha7/notification/)
|
||||
- v1-alpha8
|
||||
- [All subsystem](https://jd.engine.staropensource.de/v1-alpha8/all/)
|
||||
- [base](https://jd.engine.staropensource.de/v1-alpha8/base/)
|
||||
- [testing](https://jd.engine.staropensource.de/v1-alpha8/testing/)
|
||||
- [ansi](https://jd.engine.staropensource.de/v1-alpha8/ansi/)
|
||||
- [slf4j-compat](https://jd.engine.staropensource.de/v1-alpha8/slf4j-compat/)
|
||||
- [windowing](https://jd.engine.staropensource.de/v1-alpha8/windowing/)
|
||||
- [windowing:glfw](https://jd.engine.staropensource.de/v1-alpha8/windowing:glfw/)
|
||||
- [notification](https://jd.engine.staropensource.de/v1-alpha8/notification/)
|
||||
|
|
|
@ -13,81 +13,31 @@ Welcome to the documentation for the StarOpenSource Engine!
|
|||
|
||||
## What is it?
|
||||
The StarOpenSource Engine (or **sos!engine** for short) is a modular, extensible and easy to use Java game and application engine. \
|
||||
It is responsible for printing log messages, providing utility methods, creating and managing windows, playing audio and much more.
|
||||
It is responsible for the logging infrastructure, creating and managing windows, playing audio and much more.
|
||||
|
||||
## There are `n` different game engines and application frameworks. Why another one?
|
||||
True, there are many game engines and application frameworks out there.
|
||||
I ([JeremyStarTM](https://git.staropensource.de/JeremyStarTM)) however have never seen an engine or framework be modular,
|
||||
extendable and easy to configure at the same time. Additionally, we intend on
|
||||
supporting applications and games. This means that you don't need to remember
|
||||
different APIs for your different projects and can instead focus on just one,
|
||||
perfecting your skills along the way.
|
||||
|
||||
## Why Java?
|
||||
Java. Some say it's an awful language. We disagree, strongly.
|
||||
|
||||
While it has some pitfalls, it's almost the embodiment of
|
||||
[OOP](https://en.wikipedia.org/wiki/Object-oriented_programming),
|
||||
providing the perfect base for creating a modular engine
|
||||
and framework like the sos!engine.
|
||||
|
||||
Additionally, because of it's interpreted nature it's easy to
|
||||
modify the bytecode at runtime and perform various things
|
||||
to it before it's executed. This isn't really doable in other
|
||||
programming languages easily. Just see what Minecraft modders are doing
|
||||
to the game using the [Mixin](https://github.com/SpongePowered/Mixin)
|
||||
framework. It's a perfect example.
|
||||
|
||||
Also important to note is Java's portability.
|
||||
Yes, it has it's constrains in some cases (like when accessing files),
|
||||
but these can be worked around easily (see our `FileAccess` class for an example).
|
||||
But generally, the code is portable. There's no need to cross-compile for
|
||||
different CPU architectures and operating systems. You don't have to deal with
|
||||
compilers and their 581^51 ways of configuring them. Just `javac` your source tree,
|
||||
pack your compiled bytecode into a `.jar` and you're good to go.
|
||||
|
||||
Lastly, you can use and mix various languages and compile them to different things.
|
||||
Have Groovy code and want it to interact with Java code? That works!
|
||||
You want to use Ruby to create a game using the sos!engine?
|
||||
[You could do that](https://jruby.org). Want to compile your entire Java codebase
|
||||
to JavaScript for usable on the web? [TeaVM has you covered](https://teavm.org/).
|
||||
Want to compile your Java code into binaries for fast execution?
|
||||
[There exists GraalVM native-image](https://www.graalvm.org/latest/reference-manual/native-image/).
|
||||
Once you've written your Java code you can compile and interact with it however you like.
|
||||
As far as I know no language is as good in this aspect as Java.
|
||||
|
||||
And these are the reasons as to why we use Java over C++, Rust or other languages for example.
|
||||
While yes, performance naturally suffers a bit, the JVM and computers in general have improved
|
||||
heavily in performance over the years. From the slow thing Java once was it's almost as fast as
|
||||
compiled code on modern machines. Most of the time you don't notice the difference. And if
|
||||
performance is your concern, use GraalVM native-image as described above (note: the StarOpenSource Engine
|
||||
does not yet support native-image, see [#3](https://git.staropensource.de/StarOpenSource/Engine/issues/3)).
|
||||
## Why another one?
|
||||
Yeah, it's true that there are many game engines and frameworks out there that intend to ease development of applications and games.
|
||||
I ([JeremyStarTM](https://git.staropensource.de/JeremyStarTM)) however have never seen an engine or framework, which can be extended easily, without having to fork it and without a complicated development setup.
|
||||
And I don't mean what you make within the bounds of the engine or framework, no, I mean the engine/framework itself.
|
||||
|
||||
## Architecture of the engine
|
||||
The engine is built as a modular system, containing the core engine (called `base`)
|
||||
and various different subsystems.
|
||||
The engine consists of the core engine (`base` dependency in your project) and various subsystems (`slf4j-compat`, `windowing`, etc.). \
|
||||
\
|
||||
The job of the base engine is to provide minimal classes and interfaces needed for an application.
|
||||
It contains among other things a default logger implementation, useful methods, event system and a placeholder system. \
|
||||
\
|
||||
Subsystems on the other hand usually handle complex tasks. They provide abstractions for libraries and operating system calls. \
|
||||
"But why are there so many of them?" you might ask. Good question! Subsystems are intended to [do one thing and do it well](https://en.wikipedia.org/wiki/Unix_philosophy).
|
||||
|
||||
The job of the core engine is to provide a logging system, utility methods, ways
|
||||
for subsystems to seamlessly function and much more required for building applications.
|
||||
|
||||
Subsystems on the other hand usually handle complex tasks.
|
||||
They usually provide abstractions for libraries or handle complicated tasks.
|
||||
"But why are there so many of them?" you might ask. Good question! Subsystems
|
||||
are intended to [do one thing and do it well](https://en.wikipedia.org/wiki/Unix_philosophy).
|
||||
This avoids unnecessary bloat, having too many dependencies in your project while reducing the
|
||||
size and memory footprint of your project.
|
||||
|
||||
## Official subsystems
|
||||
Besides the `base` engine, there are two stable subsystem and two experimental subsystems.
|
||||
|
||||
There may be other subsystems out there. Please note though that they are not maintained
|
||||
by the StarOpenSource Project directly and are not automatically updated with the engine.
|
||||
## Available official subsystems
|
||||
Besides the `base` engine, there are two stable subsystem and three experimental subsystems.
|
||||
### Stable
|
||||
- [`ansi`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/ansi): Provides an ANSI logger and a ShortcodeParserSkeleton implementation for all your terminal formatting needs
|
||||
- [`slf4j-compat`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/slf4j-compat): Provides a [SLF4J](https://slf4j.org/) compatibility logger for redirecting all log calls to the engine's logging system
|
||||
- [`ansi`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/ansi): Provides an ANSI logging implementation and a ShortcodeParserSkeleton implementation
|
||||
- [`slf4j-compat`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/slf4j-compat): Provides [SLF4J](https://slf4j.org/) compatibility logger that redirects all log calls to the engine.
|
||||
### Experimental
|
||||
- [`rendering`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/rendering): Provides an API for creating, managing and rendering windows
|
||||
- [`notification`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/notification): Provides an API for sending and receiving notifications inside a program
|
||||
- [`windowing`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/windowing): Provides abstract APIs for creating and managing windows and monitors.
|
||||
- [`windowing-glfw`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/windowing/glfw): Windowing API, allowing GLFW to be used for creating windows and recieving input.
|
||||
- [`notification`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/notification): Provides an API for sending and receiving notifications inside a program.
|
||||
|
||||
## API documentation
|
||||
To read the engine API documentation, visit [jd.engine.staropensource.de](https://jd.engine.staropensource.de).
|
||||
|
|
|
@ -15,11 +15,11 @@
|
|||
"typecheck": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "3.6.1",
|
||||
"@docusaurus/plugin-client-redirects": "^3.6.1",
|
||||
"@docusaurus/plugin-content-docs": "^3.6.1",
|
||||
"@docusaurus/plugin-sitemap": "^3.6.1",
|
||||
"@docusaurus/preset-classic": "3.6.1",
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/plugin-client-redirects": "^3.5.2",
|
||||
"@docusaurus/plugin-content-docs": "^3.5.2",
|
||||
"@docusaurus/plugin-sitemap": "^3.5.2",
|
||||
"@docusaurus/preset-classic": "3.5.2",
|
||||
"@mdx-js/react": "^3.0.1",
|
||||
"clsx": "^2.1.0",
|
||||
"prism-react-renderer": "^2.3.1",
|
||||
|
@ -27,9 +27,9 @@
|
|||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "3.6.1",
|
||||
"@docusaurus/tsconfig": "3.6.1",
|
||||
"@docusaurus/types": "3.6.1",
|
||||
"@docusaurus/module-type-aliases": "3.5.2",
|
||||
"@docusaurus/tsconfig": "3.5.2",
|
||||
"@docusaurus/types": "3.5.2",
|
||||
"@types/node": "^20.12.5",
|
||||
"typescript": "~5.4.4"
|
||||
},
|
||||
|
|
|
@ -21,28 +21,28 @@
|
|||
versioningCodename=Sugarcookie
|
||||
versioningVersion=1
|
||||
versioningType=alpha
|
||||
versioningTyperelease=9
|
||||
versioningTyperelease=6
|
||||
versioningFork=
|
||||
|
||||
# Java
|
||||
javaSource=21
|
||||
javaSource=22
|
||||
javaTarget=21
|
||||
|
||||
# Plugins
|
||||
pluginShadow=8.1.8
|
||||
pluginLombok=8.10.2
|
||||
pluginShadow=8.1.7
|
||||
pluginLombok=8.6
|
||||
pluginGitProperties=2.4.2
|
||||
pluginNativeImage=v1.4.1
|
||||
pluginNativeImage=v1.4.0
|
||||
|
||||
# Dependencies
|
||||
dependencyLombok=1.18.34
|
||||
dependencyJetbrainsAnnotations=26.0.1
|
||||
dependencyLombok=1.18.32
|
||||
dependencyJetbrainsAnnotations=24.1.0
|
||||
dependencyJansi=2.4.1
|
||||
dependencyReflections=0.10.2
|
||||
dependencySlf4j=2.0.16
|
||||
dependencyLwjgl=3.3.4
|
||||
dependencySlf4j=2.0.13
|
||||
dependencyLwjgl=3.3.3
|
||||
dependencyLwjglNatives=
|
||||
dependencyJunit=5.11.3
|
||||
dependencyJunit=5.11.0-M2
|
||||
|
||||
# etc
|
||||
group = de.staropensource.engine
|
||||
|
|
|
@ -22,7 +22,6 @@ package de.staropensource.engine.notification;
|
|||
import de.staropensource.engine.base.annotation.EngineSubsystem;
|
||||
import de.staropensource.engine.base.implementable.SubsystemClass;
|
||||
import de.staropensource.engine.base.implementation.versioning.StarOpenSourceVersioningSystem;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.type.DependencyVector;
|
||||
import de.staropensource.engine.base.utility.information.EngineInformation;
|
||||
import lombok.Getter;
|
||||
|
@ -59,7 +58,7 @@ public final class NotificationSubsystem extends SubsystemClass {
|
|||
if (instance == null)
|
||||
instance = this;
|
||||
else
|
||||
Logger.crash("Only one instance of this class is allowed, use getInstance() instead of creating a new instance");
|
||||
logger.crash("Only one instance of this class is allowed, use getInstance() instead of creating a new instance");
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
|
|
|
@ -1,323 +0,0 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.rendering;
|
||||
|
||||
import de.staropensource.engine.base.annotation.EngineSubsystem;
|
||||
import de.staropensource.engine.base.annotation.EventListener;
|
||||
import de.staropensource.engine.base.implementable.SubsystemClass;
|
||||
import de.staropensource.engine.base.implementable.helper.EventHelper;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.type.EventPriority;
|
||||
import de.staropensource.engine.base.utility.Math;
|
||||
import de.staropensource.engine.base.utility.information.EngineInformation;
|
||||
import de.staropensource.engine.base.implementation.versioning.StarOpenSourceVersioningSystem;
|
||||
import de.staropensource.engine.base.event.InternalEngineShutdownEvent;
|
||||
import de.staropensource.engine.base.type.DependencyVector;
|
||||
import de.staropensource.engine.base.utility.Miscellaneous;
|
||||
import de.staropensource.engine.rendering.event.InputEvent;
|
||||
import de.staropensource.engine.rendering.event.RenderingErrorEvent;
|
||||
import de.staropensource.engine.rendering.exception.NotOnMainThreadException;
|
||||
import de.staropensource.engine.rendering.type.Window;
|
||||
import de.staropensource.engine.rendering.type.window.VsyncMode;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.*;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
|
||||
/**
|
||||
* Main class of the {@code rendering} subsystem.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
@EngineSubsystem
|
||||
@SuppressWarnings({ "JavadocDeclaration" })
|
||||
public final class RenderingSubsystem extends SubsystemClass {
|
||||
/**
|
||||
* Contains the class instance.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* -- GETTER --
|
||||
* Returns the class instance.
|
||||
*
|
||||
* @return class instance unless the subsystem is uninitialized
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
@Getter
|
||||
private static RenderingSubsystem instance = null;
|
||||
|
||||
/**
|
||||
* The {@link GLFWErrorCallback} to use.
|
||||
* <p>
|
||||
* Only declared publicly for freeing during engine shutdown.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private GLFWErrorCallback errorCallback = null;
|
||||
|
||||
// -----> Subsystem
|
||||
/**
|
||||
* Initializes this subsystem.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
public RenderingSubsystem() {
|
||||
// Only allow one instance
|
||||
if (instance == null)
|
||||
instance = this;
|
||||
else
|
||||
Logger.crash("Only one instance of this class is allowed, use getInstance() instead of creating a new instance");
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return "rendering";
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull DependencyVector getDependencyVector() {
|
||||
return new DependencyVector.Builder()
|
||||
.setIdentifier(getName())
|
||||
.setVersioningSystem(StarOpenSourceVersioningSystem.class)
|
||||
.setVersion(EngineInformation.getVersioningString())
|
||||
.build();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void initializeSubsystem() {
|
||||
if (!Miscellaneous.onMainThread())
|
||||
Logger.crash("Unable to initialize the rendering subsystem whilst running on a non-main thread", new NotOnMainThreadException());
|
||||
|
||||
// Initialize WindowingSubsystemConfiguration and load it
|
||||
new RenderingSubsystemConfiguration().loadConfiguration();
|
||||
|
||||
// Precompute event listeners
|
||||
cacheEvents();
|
||||
initGlfw();
|
||||
|
||||
// Warn about subsystem and API instability
|
||||
Logger.warn("The rendering subsystem is experimental. Subsystem and API stability are not guaranteed.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches all windowing subsystem events.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
private void cacheEvents() {
|
||||
EventHelper.cacheEvent(RenderingErrorEvent.class);
|
||||
EventHelper.cacheEvent(InputEvent.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes GLFW.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
private void initGlfw() {
|
||||
try {
|
||||
Logger.verb("Initializing GLFW");
|
||||
|
||||
// Set error callback
|
||||
Logger.diag("Setting error callback");
|
||||
errorCallback = GLFWErrorCallback.create((error, description) -> new RenderingErrorEvent().callEvent("GLFW: " + GLFWErrorCallback.getDescription(description) + " [" + error + "]")).set();
|
||||
|
||||
// Set init hints
|
||||
Logger.diag("Setting initialization hints");
|
||||
switch (RenderingSubsystemConfiguration.getInstance().getInitialPlatform()) {
|
||||
case ANY -> glfwInitHint(GLFW_PLATFORM, GLFW_ANY_PLATFORM);
|
||||
case WAYLAND -> tryPlatform(GLFW_PLATFORM_WAYLAND);
|
||||
case X11 -> tryPlatform(GLFW_PLATFORM_X11);
|
||||
case WIN32 -> tryPlatform(GLFW_PLATFORM_WIN32);
|
||||
case COCOA -> tryPlatform(GLFW_PLATFORM_COCOA);
|
||||
case NONE -> glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_NULL);
|
||||
}
|
||||
glfwInitHint(GLFW_WAYLAND_LIBDECOR, RenderingSubsystemConfiguration.getInstance().isInitialDisableLibdecor() ? GLFW_WAYLAND_DISABLE_LIBDECOR : GLFW_WAYLAND_PREFER_LIBDECOR);
|
||||
|
||||
// Initialize GLFW
|
||||
Logger.diag("Invoking glfwInit");
|
||||
if (!glfwInit())
|
||||
Logger.crash("Failed to initialize GLFW");
|
||||
} catch (UnsatisfiedLinkError error) {
|
||||
Logger.crash("Failed to load LWJGL native libraries", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts the subsystem down.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
@EventListener(event = InternalEngineShutdownEvent.class)
|
||||
@SuppressWarnings({ "unused" })
|
||||
protected static void shutdownSubsystem() {
|
||||
if (instance == null)
|
||||
return;
|
||||
|
||||
Logger.verb("Shutting down");
|
||||
|
||||
long shutdownTime = Miscellaneous.measureExecutionTime(() -> {
|
||||
// Close all windows
|
||||
for (Window window : Window.getWindows())
|
||||
window.close();
|
||||
|
||||
instance.errorCallback.free();
|
||||
glfwTerminate();
|
||||
});
|
||||
|
||||
Logger.info("Shut down in " + shutdownTime + "ms");
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs about rendering errors.
|
||||
*
|
||||
* @see RenderingSubsystemConfiguration#errorRenderingFailures
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
@EventListener(event = RenderingErrorEvent.class, priority = EventPriority.EXCLUSIVELY_IMPORTANT)
|
||||
private static void logRenderingError(@NotNull String error) {
|
||||
Logger.error("Rendering error occurred: " + error);
|
||||
}
|
||||
|
||||
// -----> APIs
|
||||
/**
|
||||
* Renders all windows once.
|
||||
* To render all windows continuously, invoke
|
||||
* {@link #runRenderLoop(Runnable)} instead.
|
||||
*
|
||||
* @return map of windows and their {@link Throwable}s
|
||||
* @throws NotOnMainThreadException if not running on the main thread
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
public LinkedHashMap<@NotNull Window, @NotNull Throwable> renderWindows() throws NotOnMainThreadException {
|
||||
// Ensure running on the main thread
|
||||
if (!Miscellaneous.onMainThread())
|
||||
throw new NotOnMainThreadException();
|
||||
|
||||
LinkedHashMap<@NotNull Window, @NotNull Throwable> throwables = new LinkedHashMap<>();
|
||||
|
||||
// Update and render all windows
|
||||
for (Window window : Window.getWindows()) {
|
||||
if (!window.isRendering())
|
||||
continue;
|
||||
|
||||
try {
|
||||
window.updateState();
|
||||
window.render();
|
||||
} catch (Throwable throwable) {
|
||||
Logger.error("Rendering window " + window + " failed: Threw throwable " + throwable.getClass().getName() + (throwable.getMessage() == null ? "" : ": " + throwable.getMessage()));
|
||||
throwables.put(window, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
// Poll for events
|
||||
glfwPollEvents();
|
||||
|
||||
return throwables;
|
||||
}
|
||||
/**
|
||||
* Renders all windows continuously.
|
||||
* To render all windows just once, invoke
|
||||
* {@link #renderWindows()} instead.
|
||||
* <p>
|
||||
* Immediately returns when a {@link #renderWindows()} call fails.
|
||||
*
|
||||
* @param frameCode code which shall be invoked before a frame is rendered
|
||||
* @return see {@link #renderWindows()} (on failure)
|
||||
* @throws NotOnMainThreadException if not running on the main thread
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
public LinkedHashMap<@NotNull Window, @NotNull Throwable> runRenderLoop(@NotNull Runnable frameCode) throws NotOnMainThreadException {
|
||||
// Ensure running on the main thread
|
||||
if (!Miscellaneous.onMainThread())
|
||||
throw new NotOnMainThreadException();
|
||||
|
||||
// Define variables
|
||||
AtomicReference<LinkedHashMap<@NotNull Window, @NotNull Throwable>> output = new AtomicReference<>(new LinkedHashMap<>()); // runRenderLoop output
|
||||
long renderTime; // Amount of time spent rendering
|
||||
long sleepDuration; // Time spent sleeping the thread
|
||||
LinkedList<Long> splitDeltaTime = new LinkedList<>(); // Used for calculating the delta time (render time average over one second)
|
||||
long reportDuration = System.currentTimeMillis() + 1000; // Used for determining when to report frame count and delta time
|
||||
double deltaTime; // Contains the average render time over one second (delta time)
|
||||
|
||||
// Check if delta time and frame count shall be printed to console.
|
||||
// Unless this code is ran 292 billion years into the future,
|
||||
// this should sufficiently disable the reporting feature.
|
||||
if (!RenderingSubsystemConfiguration.getInstance().isDebugFrames())
|
||||
reportDuration = Long.MAX_VALUE;
|
||||
|
||||
// Run while the 'output' is empty
|
||||
while (output.get().isEmpty()) {
|
||||
renderTime = Miscellaneous.measureExecutionTime(() -> {
|
||||
output.set(renderWindows());
|
||||
frameCode.run();
|
||||
});
|
||||
|
||||
if (RenderingSubsystemConfiguration.getInstance().getVsyncMode() != VsyncMode.OFF)
|
||||
// V-Sync is enabled, no need for manual busy waiting
|
||||
sleepDuration = 0L;
|
||||
else
|
||||
// Calculate amount of time the thread should spend sleeping
|
||||
sleepDuration = (long) (1d / RenderingSubsystemConfiguration.getInstance().getMaximumFramesPerSecond() * 1000d) - renderTime;
|
||||
// Add render and sleep time to list used for calculating the delta time value
|
||||
splitDeltaTime.add(renderTime + sleepDuration);
|
||||
|
||||
// Busy wait unless V-Sync is enabled
|
||||
if (RenderingSubsystemConfiguration.getInstance().getVsyncMode() == VsyncMode.OFF && RenderingSubsystemConfiguration.getInstance().getMaximumFramesPerSecond() >= 1) {
|
||||
sleepDuration += System.currentTimeMillis();
|
||||
while (System.currentTimeMillis() < sleepDuration)
|
||||
Thread.onSpinWait();
|
||||
}
|
||||
|
||||
// Calculate delta time and frame count every second
|
||||
if (System.currentTimeMillis() >= reportDuration) {
|
||||
deltaTime = Math.getMeanLong(splitDeltaTime); // Calculate delta time
|
||||
Logger.diag("Delta time average: " + deltaTime + " | Frames/s: " + 1000 / deltaTime); // Print delta time and frame count to console
|
||||
|
||||
reportDuration = System.currentTimeMillis() + 1000; // Update 'reportDuration'
|
||||
splitDeltaTime.clear(); // Clear 'splitDeltaTime' list
|
||||
}
|
||||
}
|
||||
|
||||
return output.get();
|
||||
}
|
||||
|
||||
// -----> Utility methods
|
||||
/**
|
||||
* Checks if the specified platform is compatible,
|
||||
* and if so, specifies it as the platform to use.
|
||||
*
|
||||
* @param platform platform to try
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
private void tryPlatform(int platform) {
|
||||
if (glfwPlatformSupported(platform))
|
||||
glfwInitHint(GLFW_PLATFORM, platform);
|
||||
else
|
||||
glfwInitHint(GLFW_PLATFORM, GLFW_ANY_PLATFORM);
|
||||
}
|
||||
}
|
|
@ -1,311 +0,0 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.rendering;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Configuration;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.utility.PropertiesReader;
|
||||
import de.staropensource.engine.rendering.event.RenderingErrorEvent;
|
||||
import de.staropensource.engine.rendering.type.window.RenderingPlatform;
|
||||
import de.staropensource.engine.rendering.type.window.VsyncMode;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Provides the configuration for the rendering subsystem.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
@Getter
|
||||
@SuppressWarnings({ "JavadocDeclaration" })
|
||||
public final class RenderingSubsystemConfiguration extends Configuration {
|
||||
/**
|
||||
* Contains the class instance.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* -- GETTER --
|
||||
* Returns the class instance.
|
||||
*
|
||||
* @return class instance unless {@link RenderingSubsystem} is uninitialized
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
@Getter
|
||||
private static RenderingSubsystemConfiguration instance;
|
||||
|
||||
/**
|
||||
* Contains the configuration prefix.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* -- GETTER --
|
||||
* Returns the configuration prefix.
|
||||
*
|
||||
* @return property group
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
@Getter
|
||||
public final @NotNull String group = "sosengine.rendering.";
|
||||
|
||||
/**
|
||||
* Contains if debugging options should be allowed.
|
||||
* All debugging options will be forcefully set to
|
||||
* {@code false} if this option is set to {@code false}.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* -- GETTER --
|
||||
* Returns if debugging options should be allowed.
|
||||
* All debugging options will be forcefully set to
|
||||
* {@code false} if this option is set to {@code false}.
|
||||
*
|
||||
* @return debugging enabled?
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
private boolean debug;
|
||||
|
||||
/**
|
||||
* Contains whether or not key presses
|
||||
* and releases should be logged.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* -- GETTER --
|
||||
* Returns whether or not key presses
|
||||
* and releases should be logged.
|
||||
*
|
||||
* @return log key presses and releases?
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
private boolean debugInput;
|
||||
|
||||
/**
|
||||
* Contains whether or not the delta time and
|
||||
* FPS count should be logged to the console
|
||||
* every second.
|
||||
* <p>
|
||||
* Changes will no longer be picked up as
|
||||
* soon as the rendering loop is running.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* -- GETTER --
|
||||
* Returns whether or not the delta time and
|
||||
* FPS count should be logged to the console
|
||||
* every second.
|
||||
* <p>
|
||||
* Changes will no longer be picked up as
|
||||
* soon as the rendering loop is running.
|
||||
*
|
||||
* @return print delta time and FPS count?
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
private boolean debugFrames;
|
||||
|
||||
|
||||
/**
|
||||
* Contains the platform GLFW shall try initialising.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* -- GETTER --
|
||||
* Returns the platform GLFW shall try initialising.
|
||||
*
|
||||
* @return GLFW platform
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
private RenderingPlatform initialPlatform;
|
||||
|
||||
/**
|
||||
* Contains whether or not to disable support for
|
||||
* <a href="https://gitlab.freedesktop.org/libdecor/libdecor">libdecor</a>.
|
||||
* <p>
|
||||
* Only affects the {@link RenderingPlatform#WAYLAND} platform.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* -- GETTER --
|
||||
* Returns whether or not to disable support for
|
||||
* <a href="https://gitlab.freedesktop.org/libdecor/libdecor">libdecor</a>.
|
||||
* <p>
|
||||
* Only affects the {@link RenderingPlatform#WAYLAND} platform.
|
||||
*
|
||||
* @return libdecor support disabled?
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
private boolean initialDisableLibdecor;
|
||||
|
||||
|
||||
/**
|
||||
* Contains whether or not rendering
|
||||
* errors should be logged.
|
||||
* <p>
|
||||
* The {@link RenderingErrorEvent} will
|
||||
* be emitted anyway, regardless of the
|
||||
* value of this variable.
|
||||
*
|
||||
* @see RenderingErrorEvent
|
||||
* @since v1-alpha9
|
||||
* -- GETTER --
|
||||
* Returns whether or not rendering
|
||||
* errors should be logged.
|
||||
* <p>
|
||||
* The {@link RenderingErrorEvent} will
|
||||
* be emitted anyway, regardless of the
|
||||
* value of this variable.
|
||||
*
|
||||
* @return log rendering failures?
|
||||
* @see RenderingErrorEvent
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
private boolean errorRenderingFailures;
|
||||
|
||||
|
||||
/**
|
||||
* Contains which {@link VsyncMode} to use.
|
||||
* <p>
|
||||
* This setting determines if and how V-Sync
|
||||
* will operate, which (if enabled) tries to
|
||||
* synchronize the frame rate to the monitor's
|
||||
* refresh rate. See {@link VsyncMode}
|
||||
* for more information.
|
||||
*
|
||||
* @see VsyncMode
|
||||
* @since v1-alpha9
|
||||
* -- GETTER --
|
||||
* Returns which {@link VsyncMode} to use.
|
||||
* <p>
|
||||
* This setting determines if and how V-Sync
|
||||
* will operate, which (if enabled) tries to
|
||||
* synchronize the frame rate to the monitor's
|
||||
* refresh rate. See {@link VsyncMode}
|
||||
* for more information.
|
||||
*
|
||||
* @return active V-Sync mode
|
||||
* @see VsyncMode
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
private VsyncMode vsyncMode;
|
||||
|
||||
/**
|
||||
* Contains the maximum value of frames
|
||||
* which can be rendered per second.
|
||||
* <p>
|
||||
* This value will have no effect on
|
||||
* windows with V-Sync enabled.
|
||||
* Set to {@code 0} for effectively
|
||||
* no limit. Not recommended.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* -- GETTER --
|
||||
* Returns the maximum value of frames
|
||||
* which can be rendered per second.
|
||||
* <p>
|
||||
* This value will have no effect on
|
||||
* windows with V-Sync enabled.
|
||||
* Set to {@code 0} for effectively
|
||||
* no limit. Not recommended.
|
||||
*
|
||||
* @return maximum amount of frames per second
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
private int maximumFramesPerSecond;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @see RenderingSubsystem
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
RenderingSubsystemConfiguration() {
|
||||
instance = this;
|
||||
|
||||
loadDefaultConfiguration();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected void matchProperty(@NotNull PropertiesReader parser, @NotNull String property) {
|
||||
switch (property) {
|
||||
case "debug" -> debug = parser.getBoolean(group + property);
|
||||
case "debugInput" -> debugInput = parser.getBoolean(group + property);
|
||||
case "debugFrames" -> debugFrames = parser.getBoolean(group + property);
|
||||
|
||||
case "initialPlatform" -> {
|
||||
try {
|
||||
initialPlatform = RenderingPlatform.valueOf(parser.getString(group + property).toUpperCase());
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
Logger.error("Platform " + parser.getString(group + property) + " is not valid");
|
||||
}
|
||||
}
|
||||
case "initialDisableLibdecor" -> initialDisableLibdecor = parser.getBoolean(group + property);
|
||||
|
||||
case "errorRenderingFailures" -> errorRenderingFailures = parser.getBoolean(group + property);
|
||||
|
||||
case "vsyncMode" -> {
|
||||
try {
|
||||
vsyncMode = VsyncMode.valueOf(parser.getString(group + property).toUpperCase());
|
||||
} catch (IllegalArgumentException exception) {
|
||||
Logger.error("V-Sync mode " + parser.getString(group + property) + " is not valid");
|
||||
}
|
||||
}
|
||||
case "maximumFramesPerSecond" -> maximumFramesPerSecond = parser.getInteger(group + property, true);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected void processSettings(@NotNull PropertiesReader parser) {
|
||||
// Disable all debug options if 'debug' is disabled
|
||||
if (!debug) {
|
||||
debugInput = false;
|
||||
debugFrames = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void loadDefaultConfiguration() {
|
||||
debug = false;
|
||||
debugInput = false;
|
||||
debugFrames = false;
|
||||
|
||||
initialPlatform = RenderingPlatform.ANY;
|
||||
initialDisableLibdecor = false;
|
||||
|
||||
errorRenderingFailures = true;
|
||||
|
||||
vsyncMode = VsyncMode.ON;
|
||||
maximumFramesPerSecond = 60;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @Nullable Object getSetting(@NotNull String setting) {
|
||||
switch (setting) {
|
||||
case "debug" -> { return debug; }
|
||||
case "debugInput" -> { return debugInput; }
|
||||
case "debugFrames" -> { return debugFrames; }
|
||||
|
||||
case "initialPlatform" -> { return initialPlatform; }
|
||||
case "disableLibdecor" -> { return initialDisableLibdecor; }
|
||||
|
||||
case "errorRenderingFailures" -> { return errorRenderingFailures; }
|
||||
|
||||
case "vsyncMode" -> { return vsyncMode; }
|
||||
case "maximumFramesPerSecond" -> { return maximumFramesPerSecond; }
|
||||
default -> { return null; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/**
|
||||
* The {@code rendering} subsystem, responsible for
|
||||
* initializing and managing windows and rendering APIs.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
*/
|
||||
module sosengine.rendering {
|
||||
// Dependencies
|
||||
// -> Engine
|
||||
requires transitive sosengine.base;
|
||||
// -> Libraries
|
||||
requires transitive static lombok;
|
||||
requires transitive org.jetbrains.annotations;
|
||||
requires org.lwjgl.stb;
|
||||
requires org.lwjgl.glfw;
|
||||
requires org.lwjgl.bgfx;
|
||||
|
||||
// API access
|
||||
exports de.staropensource.engine.rendering;
|
||||
exports de.staropensource.engine.rendering.event;
|
||||
exports de.staropensource.engine.rendering.exception;
|
||||
exports de.staropensource.engine.rendering.type;
|
||||
exports de.staropensource.engine.rendering.type.input;
|
||||
exports de.staropensource.engine.rendering.type.window;
|
||||
|
||||
// Reflection access
|
||||
opens de.staropensource.engine.rendering;
|
||||
opens de.staropensource.engine.rendering.event;
|
||||
opens de.staropensource.engine.rendering.exception;
|
||||
opens de.staropensource.engine.rendering.type;
|
||||
opens de.staropensource.engine.rendering.type.input;
|
||||
opens de.staropensource.engine.rendering.type.window;
|
||||
}
|
|
@ -17,12 +17,13 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
rootProject.setName("sos!engine")
|
||||
rootProject.setName("sosengine")
|
||||
|
||||
include("base")
|
||||
include("testing")
|
||||
include("ansi")
|
||||
include("slf4j-compat")
|
||||
include("notification")
|
||||
include("rendering")
|
||||
include("windowing")
|
||||
include("windowing:glfw")
|
||||
include("testapp")
|
||||
|
|
|
@ -21,9 +21,8 @@ package de.staropensource.engine.slf4j_compat;
|
|||
|
||||
import de.staropensource.engine.base.Engine;
|
||||
import de.staropensource.engine.base.EngineConfiguration;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import de.staropensource.engine.base.utility.Miscellaneous;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Marker;
|
||||
import org.slf4j.event.Level;
|
||||
|
@ -31,11 +30,19 @@ import org.slf4j.helpers.LegacyAbstractLogger;
|
|||
import org.slf4j.helpers.MessageFormatter;
|
||||
|
||||
/**
|
||||
* A SLF4J Logger that redirects all log calls to sos!engine's Logger.
|
||||
* A SLF4J Logger that redirects all log calls to sos!engine's logger.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
public final class CompatibilityLogger extends LegacyAbstractLogger {
|
||||
/**
|
||||
* Logger instance, used to print all log messages coming from SLF4J.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private final LoggerInstance logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("ENGINE").build();
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
|
@ -134,11 +141,11 @@ public final class CompatibilityLogger extends LegacyAbstractLogger {
|
|||
* @return whether the logger is enabled for the given level
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
private boolean isLevelAllowed(LogLevel level) {
|
||||
protected boolean isLevelAllowed(LogLevel level) {
|
||||
if (Engine.getInstance() == null || EngineConfiguration.getInstance() == null)
|
||||
return false;
|
||||
else
|
||||
return EngineConfiguration.getInstance().getLogLevel().compareTo(level) < 0;
|
||||
return EngineConfiguration.getInstance().getLoggerLevel().compareTo(level) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,18 +158,28 @@ public final class CompatibilityLogger extends LegacyAbstractLogger {
|
|||
* @since v1-alpha0
|
||||
*/
|
||||
private void forwardLogCall(Level level, String pattern, Object[] arguments, Throwable throwable) {
|
||||
// Only forward log calls if the subsystem is fully initialized
|
||||
if (Slf4jCompatSubsystem.isInitialized())
|
||||
Logger.handle(
|
||||
switch (level) {
|
||||
case TRACE -> LogLevel.DIAGNOSTIC;
|
||||
case DEBUG -> LogLevel.VERBOSE;
|
||||
case INFO -> LogLevel.INFORMATIONAL;
|
||||
case WARN -> LogLevel.WARNING;
|
||||
case ERROR -> LogLevel.ERROR;
|
||||
},
|
||||
Thread.currentThread().getStackTrace()[5],
|
||||
MessageFormatter.basicArrayFormat(pattern, arguments)
|
||||
);
|
||||
String message = MessageFormatter.basicArrayFormat(pattern, arguments);
|
||||
|
||||
if (Engine.getInstance() == null) {
|
||||
System.out.println(message);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove annoying Reflections messages
|
||||
if (message.contains("could not create Vfs.Dir") || message.contains("Reflections took "))
|
||||
return;
|
||||
|
||||
switch (level) {
|
||||
case TRACE -> logger.diag(message);
|
||||
case DEBUG -> logger.verb(message);
|
||||
case INFO -> logger.info(message);
|
||||
case WARN -> logger.warn(message);
|
||||
case ERROR -> {
|
||||
if (throwable == null)
|
||||
logger.error(message);
|
||||
else
|
||||
logger.crash(message, throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ public final class CompatibilityLoggerFactory implements ILoggerFactory {
|
|||
}
|
||||
|
||||
/**
|
||||
* Actually creates the Logger.
|
||||
* Actually creates the logger.
|
||||
*
|
||||
* @param name name
|
||||
* @since v1-alpha0
|
||||
|
|
|
@ -26,8 +26,12 @@ import de.staropensource.engine.base.implementation.versioning.StarOpenSourceVer
|
|||
import de.staropensource.engine.base.event.LogEvent;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.type.DependencyVector;
|
||||
import de.staropensource.engine.base.type.logging.LogLevel;
|
||||
import de.staropensource.engine.base.type.logging.LogRule;
|
||||
import de.staropensource.engine.base.type.logging.LogRuleType;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
|
@ -52,21 +56,6 @@ public final class Slf4jCompatSubsystem extends SubsystemClass {
|
|||
@Getter
|
||||
private static Slf4jCompatSubsystem instance = null;
|
||||
|
||||
/**
|
||||
* Contains whether or not the
|
||||
* subsystem is fully initialized.
|
||||
*
|
||||
* @since v1-alpha8
|
||||
* -- GETTER --
|
||||
* Returns whether or not the
|
||||
* subsystem is fully initialized.
|
||||
*
|
||||
* @return subsystem initialization status
|
||||
* @since v1-alpha8
|
||||
*/
|
||||
@Getter
|
||||
private static boolean initialized = false;
|
||||
|
||||
/**
|
||||
* Initializes this subsystem.
|
||||
*
|
||||
|
@ -77,13 +66,21 @@ public final class Slf4jCompatSubsystem extends SubsystemClass {
|
|||
if (instance == null)
|
||||
instance = this;
|
||||
else
|
||||
Logger.crash("Only one instance of this class is allowed, use getInstance() instead of creating a new instance");
|
||||
logger.crash("Only one instance of this class is allowed, use getInstance() instead of creating a new instance");
|
||||
|
||||
// Create log rules to prevent excessive log messages from the Reflections library
|
||||
Logger.disallowMessage(".*[Reflections took].*");
|
||||
Logger.disallowMessage(".*[could not create Vfs.Dir].*");
|
||||
|
||||
initialized = true;
|
||||
Logger.getActiveRules().add(new LogRule(LogRuleType.BLACKLIST) {
|
||||
@Override
|
||||
public boolean evaluate(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
return issuerClass.equals(CompatibilityLogger.class) && message.contains("Reflections took ");
|
||||
}
|
||||
});
|
||||
Logger.getActiveRules().add(new LogRule(LogRuleType.BLACKLIST) {
|
||||
@Override
|
||||
public boolean evaluate(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message) {
|
||||
return issuerClass.equals(CompatibilityLogger.class) && message.contains("could not create Vfs.Dir");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
|
@ -95,7 +92,7 @@ public final class Slf4jCompatSubsystem extends SubsystemClass {
|
|||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void initializeSubsystem() {
|
||||
LoggerFactory.getLogger(Slf4jCompatSubsystem.class).debug("If you see this then the SLF4J Compatibility Subsystem is working!");
|
||||
LoggerFactory.getLogger(CompatibilityLogger.class).debug("If you see this then the SLF4J Compatibility Subsystem is working!");
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
|
|
|
@ -39,9 +39,10 @@ dependencies {
|
|||
|
||||
// Project
|
||||
implementation(project(":base"))
|
||||
implementation(project(":rendering"))
|
||||
implementation(project(":windowing"))
|
||||
runtimeOnly(project(":ansi"))
|
||||
runtimeOnly(project(":slf4j-compat"))
|
||||
runtimeOnly(project(":windowing:glfw"))
|
||||
}
|
||||
|
||||
// Fix delombok task
|
||||
|
@ -74,13 +75,13 @@ application {
|
|||
mainClass.set("de.staropensource.engine.testapp.Main")
|
||||
applicationDefaultJvmArgs = [
|
||||
// Display GC log
|
||||
//"-Xlog:gc",
|
||||
"-Xlog:gc",
|
||||
|
||||
// Set log level to DIAGNOSTIC
|
||||
"-Dsosengine.base.logLevel=diagnostic",
|
||||
"-Dsosengine.base.loggerLevel=diagnostic",
|
||||
|
||||
// Force writing to standard output
|
||||
"-Dsosengine.base.logForceStandardOutput=true",
|
||||
"-Dsosengine.base.loggerForceStandardOutput=true",
|
||||
|
||||
// Pass classes which should be included if
|
||||
// reflective sclasspath scanning is disabled.
|
||||
|
@ -124,9 +125,9 @@ tasks.register('runNativeImage', Exec) {
|
|||
dependsOn(nativeImage)
|
||||
|
||||
args(
|
||||
//"-Xlog:gc",
|
||||
"-Dsosengine.base.logLevel=diagnostic",
|
||||
"-Dsosengine.base.logForceStandardOutput=true",
|
||||
"-Xlog:gc",
|
||||
"-Dsosengine.base.loggerLevel=diagnostic",
|
||||
"-Dsosengine.base.loggerForceStandardOutput=true",
|
||||
"-Dsosengine.base.initialForceDisableClasspathScanning=true",
|
||||
"-Dsosengine.base.initialIncludeSubsystemClasses=de.staropensource.engine.ansi.AnsiSubsystem,de.staropensource.engine.slf4j_compat.Slf4jCompatSubsystem,de.staropensource.engine.windowing.glfw.GlfwSubsystem",
|
||||
"-Djansi.mode=force",
|
||||
|
|
|
@ -23,14 +23,14 @@ import de.staropensource.engine.base.Engine;
|
|||
import de.staropensource.engine.base.annotation.EventListener;
|
||||
import de.staropensource.engine.base.implementable.EventListenerCode;
|
||||
import de.staropensource.engine.base.implementable.helper.EventHelper;
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import de.staropensource.engine.base.type.vector.Vec2i;
|
||||
import de.staropensource.engine.base.utility.Miscellaneous;
|
||||
import de.staropensource.engine.rendering.RenderingSubsystem;
|
||||
import de.staropensource.engine.rendering.event.InputEvent;
|
||||
import de.staropensource.engine.rendering.type.Window;
|
||||
import de.staropensource.engine.rendering.type.input.Key;
|
||||
import de.staropensource.engine.rendering.type.input.KeyState;
|
||||
import de.staropensource.engine.windowing.WindowingSubsystem;
|
||||
import de.staropensource.engine.windowing.event.InputEvent;
|
||||
import de.staropensource.engine.windowing.implementable.Window;
|
||||
import de.staropensource.engine.windowing.type.input.Key;
|
||||
import de.staropensource.engine.windowing.type.input.KeyState;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
@ -57,6 +57,14 @@ public final class Main {
|
|||
@Getter
|
||||
private static final Main instance = new Main();
|
||||
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
private final @NotNull LoggerInstance logger = new LoggerInstance.Builder().setClazz(getClass()).build();
|
||||
|
||||
/**
|
||||
* Contains whether or not the
|
||||
* render loop shall be terminated.
|
||||
|
@ -117,7 +125,11 @@ public final class Main {
|
|||
});
|
||||
|
||||
// Say hello to the world!
|
||||
Logger.info("Hello world!");
|
||||
logger.info("Hello world!");
|
||||
|
||||
// Choose windowing API to use
|
||||
if (!WindowingSubsystem.getInstance().setApi())
|
||||
logger.crash("No windowing API is compatible");
|
||||
|
||||
// Create window
|
||||
Window window;
|
||||
|
@ -131,17 +143,16 @@ public final class Main {
|
|||
.setPosition(new Vec2i(10, 10))
|
||||
.build();
|
||||
} catch (Throwable throwable) {
|
||||
Logger.crash("Window.Builder#build() failed", throwable);
|
||||
logger.crash("Window.Builder#build() failed", throwable);
|
||||
return;
|
||||
}
|
||||
|
||||
if (window == null)
|
||||
Logger.crash("'window' is null");
|
||||
|
||||
// Render loop
|
||||
LinkedHashMap<@NotNull Window, @NotNull Throwable> renderLoopFailures = RenderingSubsystem
|
||||
LinkedHashMap<@NotNull Window, @NotNull Throwable> renderLoopFailures = WindowingSubsystem
|
||||
.getInstance()
|
||||
.runRenderLoop(() -> {
|
||||
.getApi()
|
||||
.getManagement()
|
||||
.runRenderLoopContinuously(() -> {
|
||||
if (shutdown || window.isClosureRequested())
|
||||
Engine.getInstance().shutdown();
|
||||
});
|
||||
|
@ -160,9 +171,13 @@ public final class Main {
|
|||
.append(Miscellaneous.getStackTraceAsString(renderLoopFailures.get(windowFailed), true))
|
||||
.append("\n");
|
||||
|
||||
Logger.crash(message.toString());
|
||||
logger.crash(message.toString());
|
||||
} catch (Exception exception) {
|
||||
Logger.crash("Caught throwable in main thread:", exception);
|
||||
System.err.println("Caught throwable in main thread:");
|
||||
System.err.println(Miscellaneous.getStackTraceHeader(exception));
|
||||
System.err.println(Miscellaneous.getStackTraceAsString(exception, true));
|
||||
|
||||
Runtime.getRuntime().halt(255);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -178,7 +193,7 @@ public final class Main {
|
|||
@SuppressWarnings({ "unused" })
|
||||
private static void onInput(@Nullable Window window, @NotNull Key key, @NotNull KeyState state) {
|
||||
if (key == Key.ESCAPE && instance != null) {
|
||||
Logger.diag("ESC pressed, setting shutdown flag");
|
||||
instance.logger.diag("ESC pressed, setting shutdown flag");
|
||||
instance.shutdown = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,5 +7,5 @@
|
|||
open module sosengine.testapp {
|
||||
// Dependencies
|
||||
// -> Engine
|
||||
requires sosengine.rendering;
|
||||
requires sosengine.windowing;
|
||||
}
|
||||
|
|
109
windowing/build.gradle
Normal file
109
windowing/build.gradle
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Plugins
|
||||
plugins {
|
||||
id("java")
|
||||
id("io.freefair.lombok") version("${pluginLombok}")
|
||||
id("maven-publish")
|
||||
}
|
||||
|
||||
// Dependencies
|
||||
dependencies {
|
||||
// Lombok
|
||||
compileOnly("org.projectlombok:lombok:${dependencyLombok}")
|
||||
annotationProcessor("org.projectlombok:lombok:${dependencyLombok}")
|
||||
|
||||
// JetBrains Annotations
|
||||
compileOnly("org.jetbrains:annotations:${dependencyJetbrainsAnnotations}")
|
||||
|
||||
// Project
|
||||
implementation(project(":base"))
|
||||
}
|
||||
|
||||
// Javadoc configuration
|
||||
javadoc {
|
||||
outputs.upToDateWhen { false } // Force task execution
|
||||
dependsOn(delombok) // Make sure the source is delomboked first
|
||||
|
||||
javadoc {
|
||||
setClasspath(files(project.sourceSets.main.compileClasspath)) // Include dependencies
|
||||
|
||||
options {
|
||||
if (new File(projectDir, "src/main/javadoc/theme.css").exists())
|
||||
stylesheetFile = new File(projectDir, "src/main/javadoc/theme.css") // Theming is cool :3
|
||||
setMemberLevel(JavadocMemberLevel.PUBLIC) // Only display public stuff
|
||||
setOverview("src/main/javadoc/overview.html") // We want a custom overview page to greet the visitor
|
||||
setLocale("en_US") // 你好
|
||||
addStringOption("Xwerror", "-quiet") // Fail build on warning
|
||||
|
||||
setJFlags([
|
||||
"-Duser.language=en_US" // See above
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Include javadoc and source jar during publishing
|
||||
java {
|
||||
withJavadocJar()
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
// Build publishing configuration
|
||||
// Note: You can safely ignore any errors or warnings thrown by your IDE here
|
||||
publishing {
|
||||
repositories {
|
||||
maven {
|
||||
name = "staropensource"
|
||||
url = uri("https://mvn.staropensource.de/engine")
|
||||
credentials(org.gradle.api.credentials.PasswordCredentials)
|
||||
authentication {
|
||||
//noinspection GroovyAssignabilityCheck
|
||||
basic (BasicAuthentication)
|
||||
}
|
||||
}
|
||||
}
|
||||
publications {
|
||||
//noinspection GroovyAssignabilityCheck
|
||||
maven (MavenPublication) {
|
||||
groupId = group
|
||||
artifactId = project.getName()
|
||||
version = version
|
||||
//noinspection GroovyAssignabilityCheck
|
||||
from components.java
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fix delombok task
|
||||
delombok.doFirst {
|
||||
File target = file("${project.projectDir}/src/main/module-info.java")
|
||||
File source = file("${project.projectDir}/src/main/java/module-info.java")
|
||||
|
||||
target.delete()
|
||||
source.renameTo(target)
|
||||
}
|
||||
delombok.doLast {
|
||||
File target = file("${project.projectDir}/src/main/java/module-info.java")
|
||||
File source = file("${project.projectDir}/src/main/module-info.java")
|
||||
|
||||
target.delete()
|
||||
source.renameTo(target)
|
||||
}
|
2
windowing/glfw/README.md
Normal file
2
windowing/glfw/README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
# The `glfw` subsystem
|
||||
This subsystem provides a Windowing API using [LWJGL](https://lwjgl.org)'s [GLFW](https://glfw.org) bindings.
|
|
@ -61,17 +61,16 @@ dependencies {
|
|||
// LWJGL
|
||||
implementation(platform("org.lwjgl:lwjgl-bom:${dependencyLwjgl}"))
|
||||
implementation("org.lwjgl:lwjgl")
|
||||
implementation("org.lwjgl:lwjgl-stb")
|
||||
implementation("org.lwjgl:lwjgl-glfw")
|
||||
implementation("org.lwjgl:lwjgl-bgfx")
|
||||
implementation("org.lwjgl:lwjgl-stb")
|
||||
runtimeOnly("org.lwjgl:lwjgl::${dependencyLwjglNatives}")
|
||||
runtimeOnly("org.lwjgl:lwjgl-stb::${dependencyLwjglNatives}")
|
||||
runtimeOnly("org.lwjgl:lwjgl-glfw::${dependencyLwjglNatives}")
|
||||
runtimeOnly("org.lwjgl:lwjgl-bgfx::${dependencyLwjglNatives}")
|
||||
runtimeOnly("org.lwjgl:lwjgl-stb::${dependencyLwjglNatives}")
|
||||
if (project.dependencyLwjglNatives == "natives-macos" || project.dependencyLwjglNatives == "natives-macos-arm64") runtimeOnly("org.lwjgl:lwjgl-vulkan::${dependencyLwjglNatives}")
|
||||
|
||||
// Project
|
||||
implementation(project(":base"))
|
||||
implementation(project(":windowing"))
|
||||
}
|
||||
|
||||
// Javadoc configuration
|
||||
|
@ -97,6 +96,26 @@ javadoc {
|
|||
}
|
||||
}
|
||||
|
||||
// Unit testing configuration
|
||||
test {
|
||||
useJUnitPlatform()
|
||||
|
||||
// Pass test configuration to test VMs
|
||||
Map<String, String> testConfiguration = new HashMap<>()
|
||||
for (String property : project.properties.keySet())
|
||||
if (property.startsWith("test."))
|
||||
testConfiguration.put(property, project.properties.get(property).toString())
|
||||
systemProperties(testConfiguration)
|
||||
|
||||
setMaxParallelForks(project.hasProperty("jobs") ? Integer.parseInt((String) project.property("jobs")) : 8)
|
||||
setForkEvery(1)
|
||||
setFailFast(true)
|
||||
|
||||
testLogging {
|
||||
events("passed", "skipped", "failed")
|
||||
}
|
||||
}
|
||||
|
||||
// Include javadoc and source jar during publishing
|
||||
java {
|
||||
withJavadocJar()
|
1
windowing/glfw/gradle
Symbolic link
1
windowing/glfw/gradle
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../gradle
|
1
windowing/glfw/gradlew
vendored
Symbolic link
1
windowing/glfw/gradlew
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../gradlew
|
1
windowing/glfw/gradlew.bat
vendored
Symbolic link
1
windowing/glfw/gradlew.bat
vendored
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../gradlew.bat
|
|
@ -0,0 +1,226 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.windowing.glfw;
|
||||
|
||||
import de.staropensource.engine.base.annotation.EngineSubsystem;
|
||||
import de.staropensource.engine.base.utility.information.EngineInformation;
|
||||
import de.staropensource.engine.base.implementation.versioning.StarOpenSourceVersioningSystem;
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import de.staropensource.engine.base.type.DependencyVector;
|
||||
import de.staropensource.engine.base.utility.Miscellaneous;
|
||||
import de.staropensource.engine.windowing.WindowingSubsystem;
|
||||
import de.staropensource.engine.windowing.implementable.api.ApiClass;
|
||||
import de.staropensource.engine.windowing.implementable.api.ApiInternalClass;
|
||||
import de.staropensource.engine.windowing.implementable.api.ApiManagementClass;
|
||||
import de.staropensource.engine.windowing.event.WindowingErrorEvent;
|
||||
import de.staropensource.engine.windowing.exception.NotOnMainThreadException;
|
||||
import de.staropensource.engine.windowing.glfw.implementable.GlfwInternalClass;
|
||||
import de.staropensource.engine.windowing.glfw.implementable.GlfwManagementClass;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFWErrorCallback;
|
||||
import org.lwjgl.glfw.GLFWErrorCallbackI;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
|
||||
/**
|
||||
* The main class of the GLFW subsystem.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@EngineSubsystem
|
||||
@SuppressWarnings({ "JavadocDeclaration" })
|
||||
public final class GlfwSubsystem extends ApiClass {
|
||||
/**
|
||||
* Contains the class instance.
|
||||
*
|
||||
* @since v1-alpha0
|
||||
* -- GETTER --
|
||||
* Returns the class instance.
|
||||
*
|
||||
* @return class instance unless the subsystem is uninitialized
|
||||
* @since v1-alpha0
|
||||
*/
|
||||
@Getter
|
||||
private static GlfwSubsystem instance = null;
|
||||
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha1
|
||||
*/
|
||||
private final LoggerInstance logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("ENGINE").build();
|
||||
|
||||
/**
|
||||
* Contains the internal API class.
|
||||
*
|
||||
* @see ApiInternalClass
|
||||
* @since v1-alpha4
|
||||
* -- GETTER --
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Getter
|
||||
private ApiInternalClass internalApi;
|
||||
|
||||
/**
|
||||
* Contains the management class.
|
||||
*
|
||||
* @see ApiManagementClass
|
||||
* @since v1-alpha4
|
||||
* -- GETTER --
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Getter
|
||||
private ApiManagementClass management;
|
||||
|
||||
/**
|
||||
* The {@link GLFWErrorCallback} to use.
|
||||
* <p>
|
||||
* Only declared publicly for freeing during engine shutdown.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private GLFWErrorCallback errorCallback = null;
|
||||
|
||||
/**
|
||||
* Initializes this subsystem.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public GlfwSubsystem() {
|
||||
// Check if subsystem has already initialized
|
||||
if (instance == null)
|
||||
instance = this;
|
||||
else
|
||||
instance.logger.crash("The subsystem tried to initialize twice");
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void initializeSubsystem() {
|
||||
// Initialize configuration
|
||||
new GlfwSubsystemConfiguration();
|
||||
|
||||
// Register API
|
||||
WindowingSubsystem.getInstance().registerApi(this);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void initializeApi() {
|
||||
logger.verb("Initializing GLFW");
|
||||
try {
|
||||
if (!Miscellaneous.onMainThread()) {
|
||||
logger.crash("Unable to initialize GLFW on a non-main thread", new NotOnMainThreadException(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set error callback
|
||||
errorCallback = GLFWErrorCallback.create(new GLFWErrorCallbackI() {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void invoke(int error, long description) {
|
||||
new WindowingErrorEvent().callEvent(description + " (" + error + ")");
|
||||
}
|
||||
}).set();
|
||||
|
||||
// Set init hints
|
||||
switch (GlfwSubsystemConfiguration.getInstance().getPlatform()) {
|
||||
case ANY -> glfwInitHint(GLFW_PLATFORM, GLFW_ANY_PLATFORM);
|
||||
case WAYLAND -> tryPlatform(GLFW_PLATFORM_WAYLAND);
|
||||
case X11 -> tryPlatform(GLFW_PLATFORM_X11);
|
||||
case WIN32 -> tryPlatform(GLFW_PLATFORM_WIN32);
|
||||
case COCOA -> tryPlatform(GLFW_PLATFORM_COCOA);
|
||||
case NONE -> glfwInitHint(GLFW_PLATFORM, GLFW_PLATFORM_NULL);
|
||||
}
|
||||
glfwInitHint(GLFW_WAYLAND_LIBDECOR, GlfwSubsystemConfiguration.getInstance().isDisableLibdecor() ? GLFW_WAYLAND_DISABLE_LIBDECOR : GLFW_WAYLAND_PREFER_LIBDECOR);
|
||||
|
||||
// Initialize GLFW
|
||||
if (!glfwInit())
|
||||
logger.crash("Failed to initialize GLFW");
|
||||
|
||||
// Initialize classes
|
||||
internalApi = new GlfwInternalClass();
|
||||
management = new GlfwManagementClass();
|
||||
} catch (UnsatisfiedLinkError error) {
|
||||
logger.crash("Failed to load LWJGL native libraries", error);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void shutdownApi() {
|
||||
logger.verb("Terminating GLFW");
|
||||
|
||||
errorCallback.free();
|
||||
|
||||
if (Miscellaneous.onMainThread())
|
||||
glfwTerminate();
|
||||
else
|
||||
logger.crash("Unable to terminate GLFW on a non-main thread. Did you call Engine#shutdown or Logger#crash from another thread?", new NotOnMainThreadException(), true);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull String getName() {
|
||||
return getApiName().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public String getApiName() {
|
||||
return "GLFW";
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull DependencyVector getDependencyVector() {
|
||||
Set<@NotNull String> dependencies = new HashSet<>();
|
||||
dependencies.add("windowing");
|
||||
|
||||
return new DependencyVector.Builder()
|
||||
.setIdentifier(getName())
|
||||
.setVersioningSystem(StarOpenSourceVersioningSystem.class)
|
||||
.setVersion(EngineInformation.getVersioningString())
|
||||
.setDependencies(dependencies)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified platform is compatible,
|
||||
* and if so, specifies it as the platform to use.
|
||||
*
|
||||
* @param platform platform to try
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private void tryPlatform(int platform) {
|
||||
if (glfwPlatformSupported(platform))
|
||||
glfwInitHint(GLFW_PLATFORM, platform);
|
||||
else
|
||||
glfwInitHint(GLFW_PLATFORM, GLFW_ANY_PLATFORM);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.windowing.glfw;
|
||||
|
||||
import de.staropensource.engine.base.implementable.Configuration;
|
||||
import de.staropensource.engine.base.utility.PropertiesReader;
|
||||
import de.staropensource.engine.windowing.glfw.type.GlfwPlatform;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Provides the GLFW subsystem configuration.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@Getter
|
||||
@SuppressWarnings({ "JavadocDeclaration" })
|
||||
public final class GlfwSubsystemConfiguration extends Configuration {
|
||||
/**
|
||||
* Contains the class instance.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
* -- GETTER --
|
||||
* Returns the class instance.
|
||||
*
|
||||
* @return class instance unless {@link GlfwSubsystem} is uninitialized
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@Getter
|
||||
private static GlfwSubsystemConfiguration instance;
|
||||
|
||||
/**
|
||||
* Defines prefix properties must begin with.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
* -- GETTER --
|
||||
* Returns prefix properties must begin with.
|
||||
*
|
||||
* @return property group
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private final @NotNull String group = "sosengine.windowing.glfw.";
|
||||
|
||||
/**
|
||||
* Contains the platform GLFW will try to initialize with.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
* -- GETTER --
|
||||
* Gets the value for {@link #platform}.
|
||||
*
|
||||
* @return variable value
|
||||
* @see #platform
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private GlfwPlatform platform;
|
||||
|
||||
/**
|
||||
* If {@code true}, will disable support for
|
||||
* <a href="https://gitlab.freedesktop.org/libdecor/libdecor">libdecor</a>.
|
||||
* <p>
|
||||
* Only affects the {@link GlfwPlatform#WAYLAND} platform.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
* -- GETTER --
|
||||
* Gets the value for {@link #disableLibdecor}.
|
||||
*
|
||||
* @return variable value
|
||||
* @see #disableLibdecor
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private boolean disableLibdecor;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @see GlfwSubsystem
|
||||
* @since v1-alpha6
|
||||
*/
|
||||
GlfwSubsystemConfiguration() {
|
||||
super("ENGINE"); // TODO Wait for flexible constructor bodies (JEP 482) to be implemented into the JVM as a stable feature. We don't want to use preview features in production code.
|
||||
|
||||
// Only allow one instance
|
||||
if (instance == null)
|
||||
instance = this;
|
||||
else
|
||||
logger.crash("Only one instance of this class is allowed, use getInstance() instead of creating a new instance");
|
||||
|
||||
loadDefaultConfiguration();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected void matchProperty(@NotNull PropertiesReader parser, @NotNull String property) {
|
||||
switch (property) {
|
||||
case "platform" -> {
|
||||
try {
|
||||
platform = GlfwPlatform.valueOf(parser.getString(group + property).toUpperCase());
|
||||
} catch (IllegalArgumentException ignored) {
|
||||
System.out.println("Platform " + parser.getString(group + property) + " is not valid");
|
||||
}
|
||||
}
|
||||
case "disableLibdecor" -> disableLibdecor = parser.getBoolean(group + property);
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected void processSettings(@NotNull PropertiesReader parser) {}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void loadDefaultConfiguration() {
|
||||
platform = GlfwPlatform.ANY;
|
||||
disableLibdecor = false;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @Nullable Object getSetting(@NotNull String setting) {
|
||||
return switch (setting) {
|
||||
case "platform" -> platform;
|
||||
case "disableLibdecor" -> disableLibdecor;
|
||||
default -> null;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -17,13 +17,13 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.rendering.callback;
|
||||
package de.staropensource.engine.windowing.glfw.callback;
|
||||
|
||||
import de.staropensource.engine.rendering.type.Window;
|
||||
import de.staropensource.engine.rendering.event.InputEvent;
|
||||
import de.staropensource.engine.rendering.callback.WindowCallback;
|
||||
import de.staropensource.engine.rendering.type.input.Key;
|
||||
import de.staropensource.engine.rendering.type.input.KeyState;
|
||||
import de.staropensource.engine.windowing.implementable.Window;
|
||||
import de.staropensource.engine.windowing.event.InputEvent;
|
||||
import de.staropensource.engine.windowing.glfw.implementable.WindowCallback;
|
||||
import de.staropensource.engine.windowing.type.input.Key;
|
||||
import de.staropensource.engine.windowing.type.input.KeyState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFWKeyCallbackI;
|
||||
|
||||
|
@ -32,7 +32,7 @@ import static org.lwjgl.glfw.GLFW.*;
|
|||
/**
|
||||
* A {@link GLFWKeyCallbackI} implementation, which emits {@link InputEvent}.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public final class KeyCallback extends WindowCallback implements GLFWKeyCallbackI {
|
||||
/**
|
||||
|
@ -40,7 +40,7 @@ public final class KeyCallback extends WindowCallback implements GLFWKeyCallback
|
|||
* and making too many allocations, which would potentially decrease
|
||||
* performance.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private static final InputEvent event = new InputEvent();
|
||||
|
||||
|
@ -48,7 +48,7 @@ public final class KeyCallback extends WindowCallback implements GLFWKeyCallback
|
|||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @param window {@link Window} class
|
||||
* @since v1-alpha9
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public KeyCallback(@NotNull Window window) {
|
||||
super(window);
|
|
@ -17,12 +17,13 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.rendering.callback;
|
||||
package de.staropensource.engine.windowing.glfw.callback;
|
||||
|
||||
import de.staropensource.engine.rendering.type.Window;
|
||||
import de.staropensource.engine.rendering.event.InputEvent;
|
||||
import de.staropensource.engine.rendering.type.input.Key;
|
||||
import de.staropensource.engine.rendering.type.input.KeyState;
|
||||
import de.staropensource.engine.windowing.implementable.Window;
|
||||
import de.staropensource.engine.windowing.event.InputEvent;
|
||||
import de.staropensource.engine.windowing.glfw.implementable.WindowCallback;
|
||||
import de.staropensource.engine.windowing.type.input.Key;
|
||||
import de.staropensource.engine.windowing.type.input.KeyState;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFWMouseButtonCallbackI;
|
||||
|
||||
|
@ -31,7 +32,7 @@ import static org.lwjgl.glfw.GLFW.*;
|
|||
/**
|
||||
* A {@link GLFWMouseButtonCallbackI} implementation, which forward them to {@link InputEvent}.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public final class MouseButtonCallback extends WindowCallback implements GLFWMouseButtonCallbackI {
|
||||
/**
|
||||
|
@ -39,7 +40,7 @@ public final class MouseButtonCallback extends WindowCallback implements GLFWMou
|
|||
* and making too many allocations, which would potentially decrease
|
||||
* performance.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private static final InputEvent event = new InputEvent();
|
||||
|
||||
|
@ -47,7 +48,7 @@ public final class MouseButtonCallback extends WindowCallback implements GLFWMou
|
|||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @param window {@link Window} class
|
||||
* @since v1-alpha9
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public MouseButtonCallback(@NotNull Window window) {
|
||||
super(window);
|
||||
|
@ -71,7 +72,7 @@ public final class MouseButtonCallback extends WindowCallback implements GLFWMou
|
|||
case GLFW_MOUSE_BUTTON_RIGHT -> Key.MOUSE_RIGHT;
|
||||
case GLFW_MOUSE_BUTTON_4, GLFW_MOUSE_BUTTON_5,
|
||||
GLFW_MOUSE_BUTTON_6, GLFW_MOUSE_BUTTON_7,
|
||||
GLFW_MOUSE_BUTTON_8 -> Key.UNKNOWN_MOUSE_BUTTON;
|
||||
GLFW_MOUSE_BUTTON_8 -> Key.UNKNOWN_MOUSE;
|
||||
default -> throw new IllegalStateException("Mouse button " + key + " is invalid");
|
||||
},
|
||||
// Key state
|
|
@ -20,6 +20,6 @@
|
|||
/**
|
||||
* Callbacks, which emit {@link de.staropensource.engine.base.implementable.Event}s.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
package de.staropensource.engine.rendering.callback;
|
||||
package de.staropensource.engine.windowing.glfw.callback;
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.windowing.glfw.implementable;
|
||||
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import de.staropensource.engine.windowing.implementable.Monitor;
|
||||
import de.staropensource.engine.windowing.implementable.api.ApiInternalClass;
|
||||
import de.staropensource.engine.windowing.exception.NoMonitorsFoundException;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.PointerBuffer;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.glfwGetMonitors;
|
||||
|
||||
/**
|
||||
* The internal API class for GLFW-powered windowing APIs.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@Getter
|
||||
@SuppressWarnings({ "JavadocDeclaration" })
|
||||
public final class GlfwInternalClass implements ApiInternalClass {
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha2
|
||||
* -- GETTER --
|
||||
* Returns the {@link LoggerInstance} for this instance.
|
||||
*
|
||||
* @return logger instance
|
||||
* @see LoggerInstance
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private final LoggerInstance logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("ENGINE").build();
|
||||
|
||||
/**
|
||||
* Contains a class which extends the {@link GlfwWindow} class.
|
||||
*
|
||||
* @since v1-alpha4
|
||||
* -- GETTER --
|
||||
* {@inheritDoc}
|
||||
* -- SETTER --
|
||||
* Sets a class which extends the {@link GlfwWindow} class.
|
||||
*
|
||||
* @param windowClass new window class
|
||||
* @since v1-alpha4
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
private @NotNull Class<? extends GlfwWindow> windowClass = GlfwWindow.class;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public GlfwInternalClass() {}
|
||||
|
||||
/**
|
||||
* Returns all connected monitors.
|
||||
*
|
||||
* @return connected monitors
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@Override
|
||||
public @NotNull LinkedHashSet<@NotNull Monitor> getMonitors() throws NoMonitorsFoundException {
|
||||
PointerBuffer monitors = glfwGetMonitors();
|
||||
LinkedHashSet<@NotNull Monitor> output = new LinkedHashSet<>();
|
||||
if (monitors == null)
|
||||
throw new NoMonitorsFoundException();
|
||||
|
||||
while (monitors.hasRemaining())
|
||||
output.add(new GlfwMonitor(monitors.get()));
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.windowing.glfw.implementable;
|
||||
|
||||
import de.staropensource.engine.base.logging.LoggerInstance;
|
||||
import de.staropensource.engine.base.utility.Miscellaneous;
|
||||
import de.staropensource.engine.windowing.implementable.api.ApiManagementClass;
|
||||
import de.staropensource.engine.windowing.implementable.Window;
|
||||
import de.staropensource.engine.windowing.exception.NotOnMainThreadException;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
|
||||
/**
|
||||
* The abstract management class for GLFW-powered windowing APIs.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@Getter
|
||||
public final class GlfwManagementClass extends ApiManagementClass {
|
||||
/**
|
||||
* Contains the {@link LoggerInstance} for this class.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private final LoggerInstance logger = new LoggerInstance.Builder().setClazz(getClass()).setOrigin("ENGINE").build();
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public GlfwManagementClass() {}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean mustRunOnMainThread() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull LinkedHashMap<@NotNull Window, @NotNull Throwable> runRenderLoop() {
|
||||
// Ensure running on the main thread
|
||||
if (!Miscellaneous.onMainThread())
|
||||
throw new NotOnMainThreadException();
|
||||
|
||||
LinkedHashMap<@NotNull Window, @NotNull Throwable> throwables = new LinkedHashMap<>();
|
||||
|
||||
// Update and render all windows
|
||||
for (Window window : Window.getWindows()) {
|
||||
if (!window.isRendering())
|
||||
continue;
|
||||
|
||||
try {
|
||||
window.updateState();
|
||||
window.render();
|
||||
} catch (Throwable throwable) {
|
||||
logger.error("Rendering window " + window + " failed: Threw throwable " + throwable.getClass().getName() + (throwable.getMessage() == null ? "" : ": " + throwable.getMessage()));
|
||||
throwables.put(window, throwable);
|
||||
}
|
||||
}
|
||||
|
||||
// Poll for events
|
||||
glfwPollEvents();
|
||||
|
||||
return throwables;
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public @NotNull LinkedHashMap<@NotNull Window, @NotNull Throwable> runRenderLoopContinuously(@NotNull Runnable frameCode) {
|
||||
// Ensure running on the main thread
|
||||
if (!Miscellaneous.onMainThread())
|
||||
throw new NotOnMainThreadException();
|
||||
|
||||
return super.runRenderLoopContinuously(frameCode);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.windowing.glfw.implementable;
|
||||
|
||||
import de.staropensource.engine.base.type.vector.Vec2i;
|
||||
import de.staropensource.engine.windowing.implementable.Monitor;
|
||||
import de.staropensource.engine.windowing.exception.InvalidMonitorException;
|
||||
import lombok.SneakyThrows;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.lwjgl.glfw.GLFWVidMode;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
|
||||
/**
|
||||
* Represents a monitor. A GLFW-powered monitor. Wait... aren't monitors powered by... power?
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@SuppressWarnings({ "JavadocDeclaration" })
|
||||
public final class GlfwMonitor extends Monitor {
|
||||
/**
|
||||
* Contains the {@link #identifier} as a long.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
* -- GETTER --
|
||||
* Returns the monitor identifier as a long.
|
||||
*
|
||||
* @return monitor identifier as a long
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private final long identifierLong;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @param identifier glfw monitor pointer
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@SneakyThrows
|
||||
public GlfwMonitor(long identifier) throws InvalidMonitorException {
|
||||
// Set identifier
|
||||
setIdentifier(String.valueOf(identifier));
|
||||
identifierLong = identifier;
|
||||
|
||||
// Check if connected
|
||||
checkConnected();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@SneakyThrows
|
||||
public void checkConnected() throws InvalidMonitorException {
|
||||
super.checkConnected();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return glfwGetMonitorName(identifierLong) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the monitor name.
|
||||
*
|
||||
* @return monitor name
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@Override
|
||||
public @NotNull String getName() throws InvalidMonitorException {
|
||||
checkConnected();
|
||||
return Objects.requireNonNull(glfwGetMonitorName(identifierLong));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the monitor size.
|
||||
*
|
||||
* @return monitor size
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@Override
|
||||
public @NotNull Vec2i getSize() throws InvalidMonitorException {
|
||||
checkConnected();
|
||||
|
||||
GLFWVidMode videoMode = Objects.requireNonNull(glfwGetVideoMode(identifierLong));
|
||||
|
||||
return new Vec2i(videoMode.width(), videoMode.height());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the monitor refresh rate.
|
||||
*
|
||||
* @return monitor refresh rate
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@Override
|
||||
public short getRefreshRate() throws InvalidMonitorException {
|
||||
checkConnected();
|
||||
return (short) Objects.requireNonNull(glfwGetVideoMode(identifierLong)).refreshRate();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
* STAROPENSOURCE ENGINE SOURCE FILE
|
||||
* Copyright (c) 2024 The StarOpenSource Engine Authors
|
||||
* Licensed under the GNU Affero General Public License v3
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.windowing.glfw.implementable;
|
||||
|
||||
import de.staropensource.engine.base.logging.Logger;
|
||||
import de.staropensource.engine.base.type.vector.Vec2i;
|
||||
import de.staropensource.engine.base.utility.Miscellaneous;
|
||||
import de.staropensource.engine.windowing.WindowingSubsystemConfiguration;
|
||||
import de.staropensource.engine.windowing.event.InputEvent;
|
||||
import de.staropensource.engine.windowing.event.RenderingErrorEvent;
|
||||
import de.staropensource.engine.windowing.exception.NotOnMainThreadException;
|
||||
import de.staropensource.engine.windowing.exception.WindowCreationFailureException;
|
||||
import de.staropensource.engine.windowing.glfw.callback.KeyCallback;
|
||||
import de.staropensource.engine.windowing.glfw.callback.MouseButtonCallback;
|
||||
import de.staropensource.engine.windowing.implementable.Monitor;
|
||||
import de.staropensource.engine.windowing.implementable.Window;
|
||||
import de.staropensource.engine.windowing.type.window.VsyncMode;
|
||||
import de.staropensource.engine.windowing.type.window.WindowMode;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.glfw.GLFWImage;
|
||||
import org.lwjgl.glfw.GLFWKeyCallback;
|
||||
import org.lwjgl.glfw.GLFWMouseButtonCallback;
|
||||
import org.lwjgl.stb.STBImage;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.lwjgl.glfw.GLFW.*;
|
||||
|
||||
/**
|
||||
* Abstract class for implementing GLFW-powered windows in a windowing API.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@SuppressWarnings({ "JavadocDeclaration" })
|
||||
public final class GlfwWindow extends Window {
|
||||
/**
|
||||
* Contains the {@link #identifier} used by GLFW.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
* -- GETTER --
|
||||
* Returns the window identifier used by GLFW.
|
||||
*
|
||||
* @return GLFW identifier
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@Getter
|
||||
private long identifierLong;
|
||||
|
||||
/**
|
||||
* Contains the {@link GLFWKeyCallback} used for emitting {@link InputEvent}s.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private GLFWKeyCallback keyCallback;
|
||||
|
||||
/**
|
||||
* Contains the {@link GLFWMouseButtonCallback} used for emitting {@link InputEvent}s.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private GLFWMouseButtonCallback mouseButtonCallback;
|
||||
|
||||
// ------------------------------------------------ [ Window (de)initialization ] ------------------------------------------------ //
|
||||
/**
|
||||
* Creates and initializes an instance of this class.
|
||||
*
|
||||
* @param name name
|
||||
* @param title title
|
||||
* @param icons icons
|
||||
* @param size size
|
||||
* @param minimumSize minimum size
|
||||
* @param maximumSize maximum size
|
||||
* @param position position
|
||||
* @param windowMode window mode
|
||||
* @param monitor monitor
|
||||
* @param resizable resizable flag
|
||||
* @param borderless borderless flag
|
||||
* @param focusable focusable flag
|
||||
* @param onTop on top flag
|
||||
* @param transparent transparency flag
|
||||
* @param rendering rendering flag
|
||||
* @throws Exception stuff thrown by the {@link #initializeWindow()} and {@link #render()} methods of the implementing windowing API
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public GlfwWindow(@NotNull String name, @NotNull String title, @Nullable Path @NotNull [] icons, @NotNull Vec2i size, @NotNull Vec2i minimumSize, @NotNull Vec2i maximumSize, @NotNull Vec2i position, @NotNull WindowMode windowMode, @NotNull Monitor monitor, boolean resizable, boolean borderless, boolean focusable, boolean onTop, boolean transparent, boolean rendering) throws Exception {
|
||||
super(name, title, icons, size, minimumSize, maximumSize, position, windowMode, monitor, resizable, borderless, focusable, onTop, transparent, rendering);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
protected void initializeWindow() {
|
||||
createGlfwWindow();
|
||||
}
|
||||
|
||||
/**
|
||||
* (Re-)Creates the associated GLFW window.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public void createGlfwWindow() throws WindowCreationFailureException {
|
||||
// Ensure running on the main thread
|
||||
if (!Miscellaneous.onMainThread())
|
||||
throw new NotOnMainThreadException();
|
||||
|
||||
// Get current focus and destroy existing window
|
||||
boolean focused = true;
|
||||
if (getIdentifier() != null) {
|
||||
focused = isFocused();
|
||||
closeGlfwWindow();
|
||||
}
|
||||
|
||||
// Set window hints
|
||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // The window's visibility is later changed in setWindowState, this is just for setting up the window
|
||||
glfwWindowHint(GLFW_POSITION_X, getPosition().getX());
|
||||
glfwWindowHint(GLFW_POSITION_Y, getPosition().getY());
|
||||
glfwWindowHint(GLFW_CENTER_CURSOR, 0);
|
||||
glfwWindowHint(GLFW_FOCUSED, Miscellaneous.getIntegerizedBoolean(focused));
|
||||
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, Miscellaneous.getIntegerizedBoolean(isTransparent()));
|
||||
glfwWindowHintString(GLFW_WAYLAND_APP_ID, getName());
|
||||
glfwWindowHintString(GLFW_X11_CLASS_NAME, getName());
|
||||
glfwWindowHintString(GLFW_X11_INSTANCE_NAME, getName());
|
||||
|
||||
// Create window
|
||||
long identifier = glfwCreateWindow(getSize().getX(), getSize().getY(), getTitle(), MemoryUtil.NULL, MemoryUtil.NULL);
|
||||
if (identifier == MemoryUtil.NULL) {
|
||||
new RenderingErrorEvent().callEvent("Unable to create window: Identifier is null");
|
||||
throw new WindowCreationFailureException();
|
||||
}
|
||||
|
||||
// Set identifier
|
||||
identifierLong = identifier;
|
||||
setIdentifier(String.valueOf(identifier));
|
||||
|
||||
// Own context
|
||||
ownContext();
|
||||
|
||||
// Set swap interval based on V-Sync mode setting
|
||||
glfwSwapInterval(WindowingSubsystemConfiguration.getInstance().getVsyncMode() == VsyncMode.ON ? 1 : 0);
|
||||
|
||||
// Create callbacks
|
||||
keyCallback = GLFWKeyCallback.create(new KeyCallback(this));
|
||||
mouseButtonCallback = GLFWMouseButtonCallback.create(new MouseButtonCallback(this));
|
||||
|
||||
// Set callback
|
||||
glfwSetKeyCallback(identifierLong, keyCallback);
|
||||
glfwSetMouseButtonCallback(identifier, mouseButtonCallback);
|
||||
|
||||
// Update the window state
|
||||
setIcons(getIcons());
|
||||
setSize(getSize());
|
||||
setMinimumSize(getMinimumSize());
|
||||
setMaximumSize(getMaximumSize());
|
||||
setWindowMode(getWindowMode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the associated GLFW window.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public void closeGlfwWindow() throws WindowCreationFailureException {
|
||||
// Ensure running on the main thread
|
||||
if (!Miscellaneous.onMainThread())
|
||||
throw new NotOnMainThreadException();
|
||||
|
||||
// Close callbacks
|
||||
keyCallback.close();
|
||||
mouseButtonCallback.close();
|
||||
|
||||
// Destroy the window
|
||||
glfwDestroyWindow(identifierLong);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public void terminate() {
|
||||
setTerminated(true);
|
||||
closeGlfwWindow();
|
||||
}
|
||||
|
||||
// ------------------------------------------------ [ State updates ] ------------------------------------------------ //
|
||||
/**
|
||||
* Updates the window state.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@Override
|
||||
public void updateState() {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
// Ensure running on the main thread
|
||||
if (!Miscellaneous.onMainThread())
|
||||
throw new NotOnMainThreadException();
|
||||
|
||||
// Update window mode
|
||||
if (Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_ICONIFIED)))
|
||||
super.setWindowMode(WindowMode.MINIMIZED);
|
||||
else if (Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_MAXIMIZED)))
|
||||
super.setWindowMode(WindowMode.MAXIMIZED);
|
||||
else if (Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_VISIBLE)))
|
||||
super.setWindowMode(WindowMode.WINDOWED);
|
||||
else if (Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_VISIBLE)))
|
||||
super.setWindowMode(WindowMode.HIDDEN);
|
||||
|
||||
// Update monitor
|
||||
if (!getMonitor().isConnected()) {
|
||||
Monitor newMonitor = null;
|
||||
|
||||
for (Monitor monitor : Monitor.getMonitors())
|
||||
if (monitor.isConnected())
|
||||
newMonitor = monitor;
|
||||
|
||||
if (newMonitor == null)
|
||||
getLogger().crash("Unable to set a new target monitor for window " + getUniqueIdentifier() + " as no monitors are connected to the system");
|
||||
|
||||
setMonitor(Objects.requireNonNull(newMonitor));
|
||||
}
|
||||
|
||||
// Update vectors
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
IntBuffer width = stack.mallocInt(2);
|
||||
IntBuffer height = stack.mallocInt(2);
|
||||
|
||||
glfwGetWindowSize(identifierLong, width, height);
|
||||
super.setSize(new Vec2i(width.get(), height.get()));
|
||||
|
||||
glfwGetWindowPos(identifierLong, width, height);
|
||||
super.setPosition(new Vec2i(width.get(), height.get()));
|
||||
}
|
||||
|
||||
// Update booleans
|
||||
super.setResizable(Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_RESIZABLE)));
|
||||
super.setOnTop(Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_FLOATING)));
|
||||
super.setTransparent(Miscellaneous.getBooleanizedInteger(glfwGetWindowAttrib(identifierLong, GLFW_TRANSPARENT_FRAMEBUFFER)));
|
||||
}
|
||||
|
||||
// ------------------------------------------------ [ Rendering ] ------------------------------------------------ //
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void render() throws NotOnMainThreadException {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
// Ensure rendering is enabled
|
||||
if (!isRendering())
|
||||
return;
|
||||
|
||||
// Ensure running on the main thread
|
||||
if (!Miscellaneous.onMainThread())
|
||||
throw new NotOnMainThreadException();
|
||||
|
||||
glfwSwapBuffers(identifierLong);
|
||||
}
|
||||
|
||||
// ------------------------------------------------ [ GLFW handling ] ------------------------------------------------ //
|
||||
/**
|
||||
* Updates the OpenGL context.
|
||||
*
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public void ownContext() {
|
||||
glfwMakeContextCurrent(identifierLong);
|
||||
}
|
||||
|
||||
// ------------------------------------------------ [ Information/Action methods ] ------------------------------------------------ //
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean isClosureRequested() {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return false;
|
||||
|
||||
return glfwWindowShouldClose(identifierLong);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public boolean isFocused() {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return false;
|
||||
|
||||
return Miscellaneous.getTristatedInteger(glfwGetWindowAttrib(identifierLong, GLFW_FOCUSED)).toBoolean();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public void focus() {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
glfwFocusWindow(identifierLong);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
public void requestAttention() {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
glfwRequestWindowAttention(identifierLong);
|
||||
}
|
||||
|
||||
// ------------------------------------------------ [ Setter overrides ] ------------------------------------------------ //
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setName(@NotNull String name) {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
super.setName(name);
|
||||
createGlfwWindow();
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setTitle(@NotNull String title) {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
super.setTitle(title);
|
||||
glfwSetWindowTitle(identifierLong, title);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@ApiStatus.Experimental
|
||||
@Override
|
||||
public void setIcons(@Nullable Path @NotNull [] icons) {
|
||||
getLogger().warn("GlfwWindow#setIcons is experimental and may cause engine or JVM crashes. Here be dragons!");
|
||||
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
super.setIcons(icons);
|
||||
if (icons != null)
|
||||
try (GLFWImage.Buffer iconsBuffer = GLFWImage.malloc(icons.length)) {
|
||||
getLogger().diag("GlfwWindow#setIcons » icons.length = " + icons.length);
|
||||
|
||||
List<ByteBuffer> iconBuffers = new ArrayList<>();
|
||||
IntBuffer width = MemoryUtil.memAllocInt(1);
|
||||
IntBuffer height = MemoryUtil.memAllocInt(1);
|
||||
IntBuffer channels = MemoryUtil.memAllocInt(1);
|
||||
|
||||
for (Path filepath : icons) {
|
||||
getLogger().diag("GlfwWindow#setIcons » iterating icons » " + iconBuffers.size() + " » " + filepath);
|
||||
// Load icon
|
||||
getLogger().diag("GlfwWindow#setIcons » loading icon");
|
||||
iconBuffers.add(STBImage.stbi_load(filepath.toAbsolutePath().toString(), width, height, channels, 4));
|
||||
|
||||
if (iconBuffers.getLast() == null) {
|
||||
getLogger().warn("Icon " + iconsBuffer.position() + " could not be loaded" + (STBImage.stbi_failure_reason() == null ? "" : ": " + STBImage.stbi_failure_reason()));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Save into 'iconsBuffer'
|
||||
getLogger().diag("GlfwWindow#setIcons » saving into buffer");
|
||||
iconsBuffer
|
||||
.position(iconsBuffer.position() + 1)
|
||||
.width(width.get(0))
|
||||
.height(height.get(0))
|
||||
.pixels(iconBuffers.getLast());
|
||||
}
|
||||
getLogger().diag("GlfwWindow#setIcons » out of iteration");
|
||||
|
||||
// Set icons
|
||||
getLogger().diag("GlfwWindow#setIcons » setting position");
|
||||
iconsBuffer.position(0);
|
||||
getLogger().diag("GlfwWindow#setIcons » setting icons");
|
||||
Logger.flushLogMessages();
|
||||
glfwSetWindowIcon(identifierLong, iconsBuffer);
|
||||
|
||||
// Free icons
|
||||
getLogger().diag("GlfwWindow#setIcons » freeing icons");
|
||||
for (ByteBuffer buffer : iconBuffers)
|
||||
if (buffer != null) {
|
||||
getLogger().diag("GlfwWindow#setIcons » freeing buffer");
|
||||
STBImage.stbi_image_free(buffer);
|
||||
} else
|
||||
getLogger().diag("GlfwWindow#setIcons » skipping null buffer");
|
||||
}
|
||||
else
|
||||
getLogger().diag("GlfwWindow#setIcons » icons is null");
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setSize(@NotNull Vec2i size) {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
super.setSize(size);
|
||||
glfwSetWindowSize(identifierLong, size.getX(), size.getY());
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setMinimumSize(@NotNull Vec2i minimumSize) {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
super.setMinimumSize(minimumSize);
|
||||
glfwSetWindowSizeLimits(identifierLong, minimumSize.getX(), minimumSize.getY(), getMaximumSize().getX(), getMaximumSize().getY());
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setMaximumSize(@NotNull Vec2i maximumSize) {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
super.setMaximumSize(maximumSize);
|
||||
glfwSetWindowSizeLimits(identifierLong, getMinimumSize().getX(), getMinimumSize().getY(), maximumSize.getX(), maximumSize.getY());
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setPosition(@NotNull Vec2i position) {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
super.setPosition(position);
|
||||
glfwSetWindowSize(identifierLong, position.getX(), position.getY());
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setWindowMode(@NotNull WindowMode windowMode) {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
super.setWindowMode(windowMode);
|
||||
switch (windowMode) {
|
||||
case HIDDEN -> glfwHideWindow(identifierLong);
|
||||
case WINDOWED -> {
|
||||
glfwShowWindow(identifierLong);
|
||||
glfwRestoreWindow(identifierLong);
|
||||
}
|
||||
case MINIMIZED -> {
|
||||
glfwShowWindow(identifierLong);
|
||||
glfwIconifyWindow(identifierLong);
|
||||
}
|
||||
case MAXIMIZED -> {
|
||||
glfwShowWindow(identifierLong);
|
||||
glfwRestoreWindow(identifierLong);
|
||||
glfwMaximizeWindow(identifierLong);
|
||||
}
|
||||
case BORDERLESS_FULLSCREEN -> {
|
||||
glfwShowWindow(identifierLong);
|
||||
glfwRestoreWindow(identifierLong);
|
||||
// TODO
|
||||
}
|
||||
case EXCLUSIVE_FULLSCREEN -> {
|
||||
glfwShowWindow(identifierLong);
|
||||
glfwRestoreWindow(identifierLong);
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setResizable(boolean resizable) {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
super.setResizable(resizable);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setBorderless(boolean borderless) {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
super.setBorderless(borderless);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setFocusable(boolean focusable) {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
super.setFocusable(focusable);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setOnTop(boolean onTop) {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
super.setOnTop(onTop);
|
||||
}
|
||||
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public void setTransparent(boolean transparent) {
|
||||
// Ensure the window is not terminated
|
||||
if (isTerminated())
|
||||
return;
|
||||
|
||||
super.setTransparent(transparent);
|
||||
createGlfwWindow();
|
||||
}
|
||||
}
|
|
@ -17,9 +17,9 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package de.staropensource.engine.rendering.callback;
|
||||
package de.staropensource.engine.windowing.glfw.implementable;
|
||||
|
||||
import de.staropensource.engine.rendering.type.Window;
|
||||
import de.staropensource.engine.windowing.implementable.Window;
|
||||
import lombok.Getter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
|
@ -27,7 +27,7 @@ import org.jetbrains.annotations.NotNull;
|
|||
* Abstract class used for easily implementing
|
||||
* callbacks which require a {@link Window} instance.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
@Getter
|
||||
@SuppressWarnings({ "JavadocDeclaration" })
|
||||
|
@ -36,21 +36,21 @@ public class WindowCallback {
|
|||
* Refers to the {@link Window} instance
|
||||
* this callback is tied to.
|
||||
*
|
||||
* @since v1-alpha9
|
||||
* @since v1-alpha2
|
||||
* -- GETTER --
|
||||
* Returns the {@link Window} instance
|
||||
* this callback is tied to.
|
||||
*
|
||||
* @return attached {@link Window} instance
|
||||
* @since v1-alpha9
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
private final @NotNull Window attachedWindow;
|
||||
|
||||
/**
|
||||
* Creates and initializes an instance of this abstract class.
|
||||
* Initializes this abstract class.
|
||||
*
|
||||
* @param window {@link Window} class
|
||||
* @since v1-alpha9
|
||||
* @since v1-alpha2
|
||||
*/
|
||||
public WindowCallback(@NotNull Window window) {
|
||||
this.attachedWindow = window;
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue