Overhaul engine init procedure + add new placeholder
Some checks failed
build-and-test / build (push) Successful in 1m17s
build-and-test / generate-javadoc (push) Failing after 1m21s
build-and-test / test (push) Successful in 1m30s

This commit is contained in:
JeremyStar™ 2024-11-05 21:13:30 +01:00
parent 4281f946be
commit c533a06148
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D
8 changed files with 200 additions and 135 deletions

View file

@ -32,6 +32,7 @@ import de.staropensource.engine.base.internal.type.DependencySubsystemVector;
import de.staropensource.engine.base.logging.PrintStreamService; import de.staropensource.engine.base.logging.PrintStreamService;
import de.staropensource.engine.base.logging.*; import de.staropensource.engine.base.logging.*;
import de.staropensource.engine.base.logging.backend.async.LoggingQueue; 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.DependencyVector;
import de.staropensource.engine.base.type.EngineState; import de.staropensource.engine.base.type.EngineState;
import de.staropensource.engine.base.type.immutable.ImmutableLinkedList; import de.staropensource.engine.base.type.immutable.ImmutableLinkedList;
@ -51,7 +52,6 @@ import org.reflections.scanners.Scanners;
import org.reflections.util.ClasspathHelper; import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder; import org.reflections.util.ConfigurationBuilder;
import java.io.IOException;
import java.util.*; import java.util.*;
/** /**
@ -177,33 +177,74 @@ public final class Engine extends SubsystemClass {
/** /**
* Initializes the StarOpenSource Engine. * Initializes the StarOpenSource Engine.
* *
* @throws IllegalStateException when running in an incompatible environment * @throws RuntimeException for all exceptions thrown by this constructor
* @since v1-alpha6 * @since v1-alpha8
*/ */
private Engine() throws RuntimeException { private Engine() throws RuntimeException {
try {
instance = this; instance = this;
state = EngineState.EARLY_STARTUP; state = EngineState.EARLY_STARTUP;
long initTime = Miscellaneous.measureExecutionTime(() -> { // For measuring the initialization time
try { long initTimeEarly = System.currentTimeMillis();
de.staropensource.engine.base.logging.backend.async.LoggingThread.startThread(false); 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(); new EngineConfiguration();
EngineConfiguration.getInstance().loadConfiguration(); EngineConfiguration.getInstance().loadConfiguration();
Logger.info("Initializing engine"); // Load engine build information
initializeClasses(); // Initialize classes Logger.diag("Loading engine build information");
if (checkEnvironment()) // Check environment EngineInformation.update();
throw new IllegalStateException("Running in an incompatible environment");
ensureEnvironment(); // Prepare the environment and ensure safety
cacheEvents(); // Cache event listeners
// 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; state = EngineState.STARTUP;
initTimeEarly = System.currentTimeMillis() - initTimeEarly;
// Perform automatic subsystem initialization // Perform automatic subsystem initialization
if (EngineConfiguration.getInstance().isInitialPerformSubsystemInitialization()) { if (EngineConfiguration.getInstance().isInitialPerformSubsystemInitialization()) {
collectSubsystems(); // Collect subsystems // Collect all subsystems
Logger.diag("Collecting subsystems");
collectSubsystems();
// Initialize subsystems // Initialize subsystems
try { try {
@ -212,14 +253,28 @@ public final class Engine extends SubsystemClass {
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) { } catch (Exception exception) {
throw new RuntimeException(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.");
} }
/** /**
@ -230,54 +285,32 @@ public final class Engine extends SubsystemClass {
* @throws RuntimeException on engine initialization failure * @throws RuntimeException on engine initialization failure
* @since v1-alpha6 * @since v1-alpha6
*/ */
public static void initialize() throws IllegalStateException, RuntimeException { public static void initialize() throws RuntimeException {
try { try {
if (instance == null) if (instance == null)
new Engine(); new Engine();
} catch (RuntimeException exception) { } catch (RuntimeException exception) {
if (exception.getCause() instanceof IllegalStateException)
throw (IllegalStateException) exception.getCause();
else {
Logger.error("Engine initialization failed"); Logger.error("Engine initialization failed");
Logger.error(Miscellaneous.getStackTraceHeader(exception.getCause())); Logger.error(Miscellaneous.getStackTraceHeader(exception.getCause()));
for (String line : Miscellaneous.getStackTraceAsString(exception.getCause(), true).split("\n")) for (String line : Miscellaneous.getStackTraceAsString(exception.getCause(), true).split("\n"))
Logger.error(line); Logger.error(line);
throw new RuntimeException("Engine initialization failed", exception.getCause()); throw new RuntimeException("Engine initialization failed", exception.getCause());
} }
} }
}
/** /**
* Initializes all classes. * Checks if the running JVM implementation is not supported by the engine.
* *
* @since v1-alpha0 * @since v1-alpha8
*/ */
private void initializeClasses() throws IOException { private void checkJvmIncompatibilities() {
Logger.verb("Initializing engine classes"); if (System.getProperties().getProperty("sosengine.base.allowUnsupportedJVMInitialization", "false").equals("true")) {
Logger.warn("Skipping JVM implementation incompatibilities check");
// Initialize essential engine classes return;
new EngineInternals();
EngineInformation.update();
PlaceholderEngine.initialize();
// Non-essential engine classes
PrintStreamService.initializeStreams();
FileAccess.initializeInstances();
} }
/** // Substrate VM (GraalVM Community)
* 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")) { if (JvmInformation.getImplementationName().equals("Substrate VM") && JvmInformation.getImplementationVendor().equals("GraalVM Community")) {
Logger.error("##############################################################################################"); Logger.error("##############################################################################################");
Logger.error("## Running in Substrate VM, which is the name of the JVM used by GraalVM native-image. ##"); Logger.error("## Running in Substrate VM, which is the name of the JVM used by GraalVM native-image. ##");
@ -293,37 +326,33 @@ public final class Engine extends SubsystemClass {
Logger.error("##############################################################################################"); Logger.error("##############################################################################################");
Runtime.getRuntime().exit(255); Runtime.getRuntime().exit(255);
} }
}
/**
* Checks if reflective classpath scanning is supported.
*
* @since v1-alpha8
*/
private void checkReflectiveClasspathScanningCompatibility() {
// Check if reflective classpath scanning is supported // Check if reflective classpath scanning is supported
if (checkClasspathScanningSupport()) { if (System.getProperties().getProperty("sosengine.base.considerEnvironmentUnfriendlyToClasspathScanning", "false").equals("true")) {
Logger.warn("Running in an classpath scanning-unfriendly environment, disabling."); 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("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("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."); Logger.warn("Please consult sos!engine's documentation for more information about this issue.");
EngineInternals.getInstance().overrideReflectiveClasspathScanning(false); EngineInternals.getInstance().overrideReflectiveClasspathScanning(false);
} }
return false;
} }
/** /**
* Returns whether scanning the classpath is supported. * Checks and warns if the Java version of the
* running JVM is higher than the engine supports.
* *
* @return test results * @since v1-alpha8
* @since v1-alpha5
*/ */
private boolean checkClasspathScanningSupport() { private void checkJavaVersion() {
// This may be expanded in the future if (JvmInformation.getJavaVersion() > EngineInformation.getJavaSource())
return EngineConfiguration.getInstance().isInitialForceDisableClasspathScanning(); 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());
}
/**
* Ensures the execution safety of the environment.
*
* @since v1-alpha4
*/
private void ensureEnvironment() {
EngineInternals.getInstance().installSafetyShutdownHook(true);
} }
/** /**
@ -332,15 +361,10 @@ public final class Engine extends SubsystemClass {
* @since v1-alpha0 * @since v1-alpha0
*/ */
private void cacheEvents() { private void cacheEvents() {
Logger.diag("Caching events");
// Internal events
EventHelper.cacheEvent(InternalEngineShutdownEvent.class);
// General events
EventHelper.cacheEvent(EngineCrashEvent.class); EventHelper.cacheEvent(EngineCrashEvent.class);
EventHelper.cacheEvent(EngineShutdownEvent.class); EventHelper.cacheEvent(EngineShutdownEvent.class);
EventHelper.cacheEvent(EngineSoftCrashEvent.class); EventHelper.cacheEvent(EngineSoftCrashEvent.class);
EventHelper.cacheEvent(InternalEngineShutdownEvent.class);
EventHelper.cacheEvent(LogEvent.class); EventHelper.cacheEvent(LogEvent.class);
EventHelper.cacheEvent(ThrowableCatchEvent.class); EventHelper.cacheEvent(ThrowableCatchEvent.class);
} }
@ -427,7 +451,7 @@ public final class Engine extends SubsystemClass {
resolver.addVectors(subsystems); resolver.addVectors(subsystems);
// Resolve dependencies and get order // Resolve dependencies and get order
Logger.verb("Resolving subsystem dependencies"); Logger.diag("Resolving subsystem dependencies");
try { try {
for (DependencyVector vector : resolver.resolve().getOrder()) // smol workaround for (DependencyVector vector : resolver.resolve().getOrder()) // smol workaround
order.add((DependencySubsystemVector) vector); order.add((DependencySubsystemVector) vector);
@ -450,7 +474,7 @@ public final class Engine extends SubsystemClass {
} }
// Initialize subsystems // Initialize subsystems
Logger.verb("Initializing engine subsystems"); Logger.diag("Initializing engine subsystems");
long initTime; long initTime;
for (DependencySubsystemVector vector : subsystems) { 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() + ")");

View file

@ -128,21 +128,6 @@ public final class EngineConfiguration extends Configuration {
*/ */
private boolean initialPerformSubsystemInitialization; private boolean initialPerformSubsystemInitialization;
/**
* 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, * Will try to load the specified classes as subsystems,
* if reflective classpath scanning is disabled. * if reflective classpath scanning is disabled.
@ -352,7 +337,6 @@ public final class EngineConfiguration extends Configuration {
case "debugEvents" -> debugEvents = parser.getBoolean(group + property); case "debugEvents" -> debugEvents = parser.getBoolean(group + property);
case "initialPerformSubsystemInitialization" -> initialPerformSubsystemInitialization = parser.getBoolean(group + property); case "initialPerformSubsystemInitialization" -> initialPerformSubsystemInitialization = parser.getBoolean(group + property);
case "initialForceDisableClasspathScanning" -> initialForceDisableClasspathScanning = parser.getBoolean(group + property);
case "initialIncludeSubsystemClasses" -> { case "initialIncludeSubsystemClasses" -> {
initialIncludeSubsystemClasses = new HashSet<>(); initialIncludeSubsystemClasses = new HashSet<>();
initialIncludeSubsystemClasses.addAll(Arrays.stream(parser.getString(group + property).split(",")).toList()); initialIncludeSubsystemClasses.addAll(Arrays.stream(parser.getString(group + property).split(",")).toList());
@ -374,7 +358,7 @@ public final class EngineConfiguration extends Configuration {
try { try {
logLevel = LogLevel.valueOf(parser.getString(group + property).toUpperCase()); logLevel = LogLevel.valueOf(parser.getString(group + property).toUpperCase());
} catch (IllegalArgumentException ignored) { } catch (IllegalArgumentException ignored) {
Logger.error("The log level " + parser.getString(group + property) + " is not valid"); Logger.error("The log level '" + parser.getString(group + property) + "' is not valid");
} }
} }
case "logFeatures" -> logFeatures = Set.copyOf(Arrays.stream(parser.getString(group + property).split(",")).toList()); case "logFeatures" -> logFeatures = Set.copyOf(Arrays.stream(parser.getString(group + property).split(",")).toList());
@ -403,7 +387,6 @@ public final class EngineConfiguration extends Configuration {
debugEvents = false; debugEvents = false;
initialPerformSubsystemInitialization = true; initialPerformSubsystemInitialization = true;
initialForceDisableClasspathScanning = false;
initialIncludeSubsystemClasses = new HashSet<>(); initialIncludeSubsystemClasses = new HashSet<>();
errorShortcodeParser = true; errorShortcodeParser = true;
@ -428,7 +411,6 @@ public final class EngineConfiguration extends Configuration {
case "debugEvents" -> debugEvents; case "debugEvents" -> debugEvents;
case "initialPerformSubsystemInitialization" -> initialPerformSubsystemInitialization; case "initialPerformSubsystemInitialization" -> initialPerformSubsystemInitialization;
case "initialForceDisableClasspathScanning" -> initialForceDisableClasspathScanning;
case "initialIncludeSubsystemClasses" -> initialIncludeSubsystemClasses; case "initialIncludeSubsystemClasses" -> initialIncludeSubsystemClasses;
case "errorShortcodeParser" -> errorShortcodeParser; case "errorShortcodeParser" -> errorShortcodeParser;

View file

@ -0,0 +1,46 @@
/*
* 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;
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.
*
* @see Placeholder
* @since v1-alpha8
*/
@SuppressWarnings({ "unused" })
public final class EngineVersionCodename implements Placeholder {
/**
* Creates and initializes an instance of this event.
*
* @since v1-alpha8
*/
public EngineVersionCodename() {}
/** {@inheritDoc} */
@Override
public @NotNull String replace(@NotNull String text) {
return text.replace("%engine_version_codename%", EngineInformation.getVersioningCodename());
}
}

View file

@ -19,6 +19,8 @@
package de.staropensource.engine.base.logging; 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 de.staropensource.engine.base.type.logging.LogLevel;
import lombok.Getter; import lombok.Getter;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -164,18 +166,14 @@ public final class PrintStreamService {
/** /**
* Initializes all {@link PrintStream}s offered by this class. * Initializes all {@link PrintStream}s offered by this class.
* <p>
* Only works during early engine startup.
* *
* @since v1-alpha4 * @since v1-alpha8
*/ */
public static void initializeStreams() { public static void initializeStreams() {
// Close all existing streams if (Engine.getInstance() == null || Engine.getInstance().getState() != EngineState.EARLY_STARTUP)
if (diag != null) diag.close(); return;
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 // Create streams
diag = LogStream.createPrintStream(LogLevel.DIAGNOSTIC); diag = LogStream.createPrintStream(LogLevel.DIAGNOSTIC);

View file

@ -222,10 +222,6 @@ public final class CrashHandler {
.append(EngineConfiguration.getInstance().isInitialPerformSubsystemInitialization()) .append(EngineConfiguration.getInstance().isInitialPerformSubsystemInitialization())
.append("'\n") .append("'\n")
.append("EngineConfiguration#initialForceDisableClasspathScanning='")
.append(EngineConfiguration.getInstance().isInitialForceDisableClasspathScanning())
.append("'\n")
.append("EngineConfiguration#initialIncludeSubsystemClasses='") .append("EngineConfiguration#initialIncludeSubsystemClasses='")
.append(EngineConfiguration.getInstance().getInitialIncludeSubsystemClasses()) .append(EngineConfiguration.getInstance().getInitialIncludeSubsystemClasses())
.append("'\n") .append("'\n")

View file

@ -53,7 +53,7 @@ public final class Processor {
* @since v1-alpha8 * @since v1-alpha8
*/ */
public static boolean isFeatureEnabled(@NotNull String feature) { public static boolean isFeatureEnabled(@NotNull String feature) {
return EngineConfiguration.getInstance().getLogFeatures().contains(feature); return EngineConfiguration.getInstance() != null && EngineConfiguration.getInstance().getLogFeatures().contains(feature);
} }
/** /**
@ -66,7 +66,7 @@ public final class Processor {
* @since v1-alpha8 * @since v1-alpha8
*/ */
public static void handle(@NotNull LogLevel level, @NotNull StackTraceElement issuer, @NotNull String message) { public static void handle(@NotNull LogLevel level, @NotNull StackTraceElement issuer, @NotNull String message) {
if (EngineConfiguration.getInstance().isOptimizeLogging()) if (EngineConfiguration.getInstance() != null && EngineConfiguration.getInstance().isOptimizeLogging())
LoggingQueue.add(level, issuer, message); LoggingQueue.add(level, issuer, message);
else else
process(level, issuer, message); process(level, issuer, message);
@ -84,7 +84,18 @@ public final class Processor {
StringBuilder output = new StringBuilder(); StringBuilder output = new StringBuilder();
// Filter out // Filter out
if (level.compareTo(EngineConfiguration.getInstance().getLogLevel()) < 0) 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; return;
for (String classNameDisallowed : Filterer.disallowedClasses) for (String classNameDisallowed : Filterer.disallowedClasses)

View file

@ -44,18 +44,25 @@ public final class LoggingThread {
* @since v1-alpha8 * @since v1-alpha8
*/ */
private static final @NotNull Runnable threadCode = () -> { private static final @NotNull Runnable threadCode = () -> {
int pollingSpeed;
while (!( while (!(
Thread.currentThread().isInterrupted() Thread.currentThread().isInterrupted()
|| !(EngineConfiguration.getInstance() == null || EngineConfiguration.getInstance().isOptimizeLogging()) || !(EngineConfiguration.getInstance() == null || EngineConfiguration.getInstance().isOptimizeLogging())
|| Engine.getInstance().getState() == EngineState.SHUTDOWN || Engine.getInstance().getState() == EngineState.SHUTDOWN
|| Engine.getInstance().getState() == EngineState.CRASHED || Engine.getInstance().getState() == EngineState.CRASHED
)) { )) {
if (EngineConfiguration.getInstance() == null)
pollingSpeed = 5;
else
pollingSpeed = EngineConfiguration.getInstance().getLogPollingSpeed();
// Flush all log messages // Flush all log messages
LoggingQueue.flush(); LoggingQueue.flush();
// Sleep for whatever has been configured // Sleep for whatever has been configured
if (EngineConfiguration.getInstance().getLogPollingSpeed() > 0) { if (pollingSpeed > 0) {
long sleepDuration = System.currentTimeMillis() + EngineConfiguration.getInstance().getLogPollingSpeed(); long sleepDuration = System.currentTimeMillis() + pollingSpeed;
while (System.currentTimeMillis() < sleepDuration) while (System.currentTimeMillis() < sleepDuration)
Thread.onSpinWait(); Thread.onSpinWait();
} }

View file

@ -97,6 +97,7 @@ public final class PlaceholderEngine {
placeholders.add(new EngineGitDirty()); placeholders.add(new EngineGitDirty());
// engine_version* // engine_version*
placeholders.add(new EngineVersion()); placeholders.add(new EngineVersion());
placeholders.add(new EngineVersionCodename());
placeholders.add(new EngineVersionVersion()); placeholders.add(new EngineVersionVersion());
placeholders.add(new EngineVersionType()); placeholders.add(new EngineVersionType());
placeholders.add(new EngineVersionTyperelease()); placeholders.add(new EngineVersionTyperelease());