Compare commits

..

No commits in common. "develop" and "v1-alpha8" have entirely different histories.

135 changed files with 5785 additions and 4058 deletions

3
.gitignore vendored
View file

@ -42,6 +42,3 @@ bin/
### Mac OS ###
.DS_Store
### Java ###
hs_err_pid*.log

View file

@ -72,8 +72,6 @@ To change them, simply append `-P<property>` or `-P<property>=<value>`, like thi
#### Parallelism
Use the `jobs` property to control how many jobs will get executed simultaneously.
On Linux, specify `-Pjobs=$(nproc)`. Defaults to `8`.
#### Rendering
You can use the `renderingPlatform` property to control which rendering platform to initialize for.
#### JVM Home
You can use the `graalHome` property to specify the `$JAVA_HOME` of your local GraalVM installation.
Only used in the `nativeImage` task. Useful if you aren't using GraalVM as your primary JDK.

View file

@ -35,9 +35,10 @@ 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.misc.Miscellaneous;
import de.staropensource.engine.base.utility.Miscellaneous;
import de.staropensource.engine.base.utility.PlaceholderEngine;
import de.staropensource.engine.base.utility.information.EngineInformation;
import de.staropensource.engine.base.utility.information.JvmInformation;
@ -113,7 +114,7 @@ public final class Engine extends SubsystemClass {
* @since v1-alpha1
*/
@Getter
private @NotNull List<@NotNull DependencySubsystemVector> subsystems = Collections.emptyList();
private @NotNull ImmutableLinkedList<@NotNull DependencySubsystemVector> subsystems = new ImmutableLinkedList<>();
/**
* Contains the engine's shutdown handler.
@ -290,8 +291,8 @@ public final class Engine extends SubsystemClass {
new Engine();
} catch (RuntimeException exception) {
Logger.error("Engine initialization failed");
Logger.error(Miscellaneous.throwableHeader(exception.getCause()));
for (String line : Miscellaneous.stacktraceAsString(exception.getCause(), true).split("\n"))
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());
@ -365,6 +366,7 @@ public final class Engine extends SubsystemClass {
EventHelper.cacheEvent(EngineSoftCrashEvent.class);
EventHelper.cacheEvent(InternalEngineShutdownEvent.class);
EventHelper.cacheEvent(LogEvent.class);
EventHelper.cacheEvent(ThrowableCatchEvent.class);
}
/**
@ -399,7 +401,7 @@ public final class Engine extends SubsystemClass {
}
// Update 'subsystems'
subsystems = Collections.unmodifiableList(subsystemsMutable);
subsystems = new ImmutableLinkedList<>(subsystemsMutable);
}
/**
@ -486,7 +488,7 @@ public final class Engine extends SubsystemClass {
}
// Update 'subsystems'
subsystems = Collections.unmodifiableList(order);
subsystems = new ImmutableLinkedList<>(order);
}
/**

View file

@ -19,11 +19,8 @@
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.Logger;
import de.staropensource.engine.base.logging.backend.async.LoggingThread;
import de.staropensource.engine.base.type.EngineState;
@ -69,11 +66,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,196 +79,132 @@ 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.
* 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 ShortcodeParser
* @see #logLevel
* @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 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 #logLevel}.
*
* @return minimum allowed log level
* @return variable value
* @see #logLevel
* @since v1-alpha0
*/
private LogLevel logLevel;
@ -293,118 +226,75 @@ public final class EngineConfiguration extends Configuration {
* <li><code>lineNumber</code></li>
* </ul>
*
* @see Logger
* @since v1-alpha8
* -- 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 #logFeatures}
*
* @return optional features to enable
* @return variable value
* @see #logFeatures
* @since v1-alpha8
*/
private Set<@NotNull String> logFeatures;
/**
* 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 #logPollingSpeed}.
*
* @return logging thread polling speed in milliseconds
* @see #isOptimizeLogging()
* @return variable value
* @see #logPollingSpeed
* @since v1-alpha4
*/
private int logPollingSpeed;
/**
* 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 sos!engine's logging infrastructure 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 the {@code ERROR} and {@code CRASH} log levels.
*
* @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 #logForceStandardOutput}.
*
* @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 #logForceStandardOutput
* @since v1-alpha0
*/
private boolean logForceStandardOutput;
/**
* 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;

View file

@ -20,11 +20,11 @@
package de.staropensource.engine.base;
import de.staropensource.engine.base.exception.IllegalAccessException;
import de.staropensource.engine.base.implementable.Event;
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.type.EventPriority;
import de.staropensource.engine.base.type.InternalAccessArea;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
@ -77,7 +77,7 @@ public final class EngineInternals {
* use case and application.
*
* @see EventHelper#registerEvent(Class, EventListenerCode)
* @see EventHelper#registerEvent(Class, EventListenerCode, Event.Priority)
* @see EventHelper#registerEvent(Class, EventListenerCode, EventPriority)
* @since v1-alpha5
*/
private boolean reflectiveClasspathScanning = true;
@ -191,7 +191,7 @@ public final class EngineInternals {
* @return reflective classpath scanning flag state
* @throws IllegalAccessException when restricted ({@link InternalAccessArea#REFLECTIVE_CLASSPATH_SCANNING_GET})
* @see EventHelper#registerEvent(Class, EventListenerCode)
* @see EventHelper#registerEvent(Class, EventListenerCode, Event.Priority)
* @see EventHelper#registerEvent(Class, EventListenerCode, EventPriority)
* @since v1-alpha5
*/
public boolean getReflectiveClasspathScanning() throws IllegalAccessException {
@ -216,7 +216,7 @@ public final class EngineInternals {
* @param reflectiveClasspathScanning new reflective classpath scanning
* @throws IllegalAccessException when restricted ({@link InternalAccessArea#REFLECTIVE_CLASSPATH_SCANNING_OVERRIDE})
* @see EventHelper#registerEvent(Class, EventListenerCode)
* @see EventHelper#registerEvent(Class, EventListenerCode, Event.Priority)
* @see EventHelper#registerEvent(Class, EventListenerCode, EventPriority)
* @since v1-alpha5
*/
public void overrideReflectiveClasspathScanning(boolean reflectiveClasspathScanning) throws IllegalAccessException {

View file

@ -20,6 +20,7 @@
package de.staropensource.engine.base.annotation;
import de.staropensource.engine.base.implementable.Event;
import de.staropensource.engine.base.type.EventPriority;
import org.jetbrains.annotations.NotNull;
import java.lang.annotation.*;
@ -47,8 +48,8 @@ public @interface EventListener {
* Specifies the priority of the event.
*
* @return event priority
* @see Event.Priority
* @see EventPriority
* @since v1-alpha0
*/
@NotNull Event.Priority priority() default Event.Priority.DEFAULT;
@NotNull EventPriority priority() default EventPriority.DEFAULT;
}

View file

@ -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.event;
import de.staropensource.engine.base.implementable.Event;
import de.staropensource.engine.base.implementable.helper.EventHelper;
import de.staropensource.engine.base.utility.Miscellaneous;
import org.jetbrains.annotations.NotNull;
/**
* Called when an exception is caught by {@link Miscellaneous#executeSafely(Runnable, String)}.
*
* @see Miscellaneous#executeSafely(Runnable, String)
* @since v1-alpha0
*/
public final class ThrowableCatchEvent implements Event {
/**
* Creates and initializes an instance of this event.
*
* @since v1-alpha0
*/
public ThrowableCatchEvent() {}
/**
* {@inheritDoc}
*
* @deprecated use the {@code callEvent} method with arguments
* @see ThrowableCatchEvent#callEvent(Throwable, String)
*/
@Deprecated
@Override
public void callEvent() {}
/**
* Emits the event and calls all event listeners.
*
* @param throwable caught throwable
* @param identifier an identifier given to the runnable
* @since v1-alpha0
*/
public void callEvent(@NotNull Throwable throwable, @NotNull String identifier) {
EventHelper.invokeAnnotatedMethods(getClass(), throwable, identifier);
}
}

View file

@ -20,7 +20,7 @@
package de.staropensource.engine.base.exception.reflection;
import de.staropensource.engine.base.type.reflection.ClassType;
import de.staropensource.engine.base.utility.misc.ListFormatter;
import de.staropensource.engine.base.utility.ListFormatter;
import org.jetbrains.annotations.NotNull;
/**

View file

@ -19,7 +19,6 @@
package de.staropensource.engine.base.implementable;
import de.staropensource.engine.base.annotation.EventListener;
import de.staropensource.engine.base.implementable.helper.EventHelper;
/**
@ -35,76 +34,4 @@ public interface Event {
* @since v1-alpha0
*/
void callEvent();
/**
* Specifies in which order {@link EventListener}s shall be called.
*
* @since v1-alpha9
*/
@SuppressWarnings({ "unused" })
enum Priority {
/**
* {@link EventListener}s with this
* priority are called before all others.
* <p>
* This priority is exclusive to the
* engine and subsystems and should
* not be used by applications.
*
* @since v1-alpha9
*/
EXCLUSIVELY_IMPORTANT,
/**
* {@link EventListener}s with
* this priority are called 2nd.
*
* @since v1-alpha9
*/
VERY_IMPORTANT,
/**
* {@link EventListener}s with
* this priority are called 3rd.
*
* @since v1-alpha9
*/
IMPORTANT,
/**
* {@link EventListener}s with
* this priority are called 4th.
*
* @since v1-alpha9
*/
DEFAULT,
/**
* {@link EventListener}s with
* this priority are called 5th.
*
* @since v1-alpha9
*/
UNIMPORTANT,
/**
* {@link EventListener}s with
* this priority are called 6th.
*
* @since v1-alpha9
*/
VERY_UNIMPORTANT,
/**
* {@link EventListener}s with this
* priority are called after all others.
* <p>
* This priority is exclusive to the
* engine and subsystems and should
* not be used by applications.
*
* @since v1-alpha9
*/
EXCLUSIVELY_UNIMPORTANT
}
}

View file

@ -20,6 +20,7 @@
package de.staropensource.engine.base.implementable;
import de.staropensource.engine.base.implementable.helper.EventHelper;
import de.staropensource.engine.base.type.EventPriority;
import org.jetbrains.annotations.NotNull;
/**
@ -38,7 +39,7 @@ public abstract class EventListenerCode {
*
* @since v1-alpha5
*/
public @NotNull Event.Priority priority = Event.Priority.DEFAULT;
public @NotNull EventPriority priority = EventPriority.DEFAULT;
/**
* Creates and initializes an instance of this abstract class.

View file

@ -32,7 +32,8 @@ 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.utility.misc.ListFormatter;
import de.staropensource.engine.base.type.EventPriority;
import de.staropensource.engine.base.utility.ListFormatter;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -77,7 +78,7 @@ public final class EventHelper {
* @see EngineInternals#getReflectiveClasspathScanning()
* @since v1-alpha5
*/
public static synchronized void registerEvent(@NotNull Class<? extends Event> event, @NotNull EventListenerCode eventListener, @NotNull Event.Priority priority) {
public static synchronized void registerEvent(@NotNull Class<? extends Event> event, @NotNull EventListenerCode eventListener, @NotNull EventPriority priority) {
if (EngineInternals.getInstance().getReflectiveClasspathScanning())
return;
@ -111,7 +112,7 @@ public final class EventHelper {
* @since v1-alpha5
*/
public static void registerEvent(@NotNull Class<? extends Event> event, @NotNull EventListenerCode eventListener) {
registerEvent(event, eventListener, Event.Priority.DEFAULT);
registerEvent(event, eventListener, EventPriority.DEFAULT);
}
/**

View file

@ -22,7 +22,7 @@ package de.staropensource.engine.base.implementation.versioning;
import de.staropensource.engine.base.implementable.VersioningSystem;
import de.staropensource.engine.base.exception.versioning.IncompatibleVersioningSystemException;
import de.staropensource.engine.base.exception.versioning.InvalidVersionStringException;
import de.staropensource.engine.base.utility.misc.StringUtil;
import de.staropensource.engine.base.utility.Miscellaneous;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range;
@ -98,7 +98,7 @@ public final class FourNumberVersioningSystem implements VersioningSystem {
*/
public FourNumberVersioningSystem(@NotNull String versionString) throws InvalidVersionStringException {
String[] separatorList = new String[]{ ".", "-" };
String separator = StringUtil.getSeparatorRequired(versionString, separatorList, 3);
String separator = Miscellaneous.getSeparator(versionString, separatorList, 3);
// Escape separator or throw error if invalid
switch (separator) {

View file

@ -22,7 +22,7 @@ package de.staropensource.engine.base.implementation.versioning;
import de.staropensource.engine.base.implementable.VersioningSystem;
import de.staropensource.engine.base.exception.versioning.IncompatibleVersioningSystemException;
import de.staropensource.engine.base.exception.versioning.InvalidVersionStringException;
import de.staropensource.engine.base.utility.misc.StringUtil;
import de.staropensource.engine.base.utility.Miscellaneous;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -117,7 +117,7 @@ public final class SemanticVersioningSystem implements VersioningSystem {
*/
public SemanticVersioningSystem(@NotNull String versionString) throws InvalidVersionStringException {
String[] separatorList = new String[]{ "." };
String separator = StringUtil.getSeparatorRequired(versionString, separatorList, 2);
String separator = Miscellaneous.getSeparator(versionString, separatorList, 2);
// Escape separator or throw error if invalid
switch (separator) {

View file

@ -22,7 +22,7 @@ package de.staropensource.engine.base.implementation.versioning;
import de.staropensource.engine.base.implementable.VersioningSystem;
import de.staropensource.engine.base.exception.versioning.IncompatibleVersioningSystemException;
import de.staropensource.engine.base.exception.versioning.InvalidVersionStringException;
import de.staropensource.engine.base.utility.misc.StringUtil;
import de.staropensource.engine.base.utility.Miscellaneous;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range;
@ -86,7 +86,7 @@ public final class ThreeNumberVersioningSystem implements VersioningSystem {
*/
public ThreeNumberVersioningSystem(@NotNull String versionString) throws InvalidVersionStringException {
String[] separatorList = new String[]{ ".", "-" };
String separator = StringUtil.getSeparatorRequired(versionString, separatorList, 2);
String separator = Miscellaneous.getSeparator(versionString, separatorList, 2);
// Escape separator or throw error if invalid
switch (separator) {

View file

@ -22,7 +22,7 @@ package de.staropensource.engine.base.implementation.versioning;
import de.staropensource.engine.base.implementable.VersioningSystem;
import de.staropensource.engine.base.exception.versioning.IncompatibleVersioningSystemException;
import de.staropensource.engine.base.exception.versioning.InvalidVersionStringException;
import de.staropensource.engine.base.utility.misc.StringUtil;
import de.staropensource.engine.base.utility.Miscellaneous;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range;
@ -74,7 +74,7 @@ public final class TwoNumberVersioningSystem implements VersioningSystem {
*/
public TwoNumberVersioningSystem(@NotNull String versionString) throws InvalidVersionStringException {
String[] separatorList = new String[]{ ".", "-" };
String separator = StringUtil.getSeparatorRequired(versionString, separatorList, 1);
String separator = Miscellaneous.getSeparator(versionString, separatorList, 1);
// Escape separator or throw error if invalid
switch (separator) {

View file

@ -20,7 +20,7 @@
package de.staropensource.engine.base.internal.implementation.placeholder;
import de.staropensource.engine.base.implementable.Placeholder;
import de.staropensource.engine.base.utility.misc.NumberUtil;
import de.staropensource.engine.base.utility.Math;
import org.jetbrains.annotations.NotNull;
import java.util.Calendar;
@ -43,6 +43,6 @@ public final class DateDay implements Placeholder {
/** {@inheritDoc} */
@Override
public @NotNull String replace(@NotNull String text) {
return text.replace("%date_day%", NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.DAY_OF_MONTH), 2));
return text.replace("%date_day%", Math.padNumbers(Calendar.getInstance().get(Calendar.DAY_OF_MONTH), 2));
}
}

View file

@ -20,7 +20,7 @@
package de.staropensource.engine.base.internal.implementation.placeholder;
import de.staropensource.engine.base.implementable.Placeholder;
import de.staropensource.engine.base.utility.misc.NumberUtil;
import de.staropensource.engine.base.utility.Math;
import org.jetbrains.annotations.NotNull;
import java.util.Calendar;
@ -43,6 +43,6 @@ public final class DateMonth implements Placeholder {
/** {@inheritDoc} */
@Override
public @NotNull String replace(@NotNull String text) {
return text.replace("%date_month%", NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.MONTH), 2));
return text.replace("%date_month%", Math.padNumbers(Calendar.getInstance().get(Calendar.MONTH), 2));
}
}

View file

@ -20,7 +20,7 @@
package de.staropensource.engine.base.internal.implementation.placeholder;
import de.staropensource.engine.base.implementable.Placeholder;
import de.staropensource.engine.base.utility.misc.NumberUtil;
import de.staropensource.engine.base.utility.Math;
import org.jetbrains.annotations.NotNull;
import java.util.Calendar;
@ -43,6 +43,6 @@ public final class DateYear implements Placeholder {
/** {@inheritDoc} */
@Override
public @NotNull String replace(@NotNull String text) {
return text.replace("%date_year%", NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.YEAR), 4));
return text.replace("%date_year%", Math.padNumbers(Calendar.getInstance().get(Calendar.YEAR), 4));
}
}

View file

@ -20,7 +20,7 @@
package de.staropensource.engine.base.internal.implementation.placeholder;
import de.staropensource.engine.base.implementable.Placeholder;
import de.staropensource.engine.base.utility.misc.NumberUtil;
import de.staropensource.engine.base.utility.Math;
import org.jetbrains.annotations.NotNull;
/**
@ -42,6 +42,6 @@ public final class TimeEpoch implements Placeholder {
/** {@inheritDoc} */
@Override
public @NotNull String replace(@NotNull String text) {
return text.replace("%time_epoch%", NumberUtil.padNumbers(System.currentTimeMillis(), String.valueOf(Long.MAX_VALUE).length()));
return text.replace("%time_epoch%", Math.padNumbers(System.currentTimeMillis(), String.valueOf(Long.MAX_VALUE).length()));
}
}

View file

@ -20,7 +20,7 @@
package de.staropensource.engine.base.internal.implementation.placeholder;
import de.staropensource.engine.base.implementable.Placeholder;
import de.staropensource.engine.base.utility.misc.NumberUtil;
import de.staropensource.engine.base.utility.Math;
import org.jetbrains.annotations.NotNull;
import java.util.Calendar;
@ -43,6 +43,6 @@ public final class TimeHour implements Placeholder {
/** {@inheritDoc} */
@Override
public @NotNull String replace(@NotNull String text) {
return text.replace("%time_hour%", NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.HOUR_OF_DAY), 2));
return text.replace("%time_hour%", Math.padNumbers(Calendar.getInstance().get(Calendar.HOUR_OF_DAY), 2));
}
}

View file

@ -20,7 +20,7 @@
package de.staropensource.engine.base.internal.implementation.placeholder;
import de.staropensource.engine.base.implementable.Placeholder;
import de.staropensource.engine.base.utility.misc.NumberUtil;
import de.staropensource.engine.base.utility.Math;
import org.jetbrains.annotations.NotNull;
import java.util.Calendar;
@ -43,6 +43,6 @@ public final class TimeMinute implements Placeholder {
/** {@inheritDoc} */
@Override
public @NotNull String replace(@NotNull String text) {
return text.replace("%time_minute%", NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.MINUTE), 2));
return text.replace("%time_minute%", Math.padNumbers(Calendar.getInstance().get(Calendar.MINUTE), 2));
}
}

View file

@ -20,7 +20,7 @@
package de.staropensource.engine.base.internal.implementation.placeholder;
import de.staropensource.engine.base.implementable.Placeholder;
import de.staropensource.engine.base.utility.misc.NumberUtil;
import de.staropensource.engine.base.utility.Math;
import org.jetbrains.annotations.NotNull;
import java.util.Calendar;
@ -43,6 +43,6 @@ public final class TimeSecond implements Placeholder {
/** {@inheritDoc} */
@Override
public @NotNull String replace(@NotNull String text) {
return text.replace("%time_second%", NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.SECOND), 2));
return text.replace("%time_second%", Math.padNumbers(Calendar.getInstance().get(Calendar.SECOND), 2));
}
}

View file

@ -19,12 +19,15 @@
package de.staropensource.engine.base.logging;
import de.staropensource.engine.base.EngineConfiguration;
import de.staropensource.engine.base.implementable.LoggingAdapter;
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.type.logging.LogLevel;
import lombok.Getter;
import lombok.Setter;
@ -32,6 +35,8 @@ import org.intellij.lang.annotations.RegExp;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
/**
* The frontend class for sos!engine's logging system.
*

View file

@ -24,8 +24,8 @@ 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.misc.NumberUtil;
import de.staropensource.engine.base.utility.misc.Miscellaneous;
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;
@ -107,13 +107,13 @@ public final class CrashHandler {
output
.append("\nCaused by:")
.append("\nNo throwable has been passed.");
else {
else
output
.append("\n")
.append(Miscellaneous.stacktraceAsStringRecursive(throwable, true, true))
.append(Miscellaneous.getStackTraceHeader(throwable))
.append("\n")
.append(Miscellaneous.getStackTraceAsString(throwable, true))
.append("\n");
}
output.append("\nMessage: \n")
.append(message)
@ -123,23 +123,23 @@ public final class CrashHandler {
output
.append("---- Environment ----")
.append("\nTime and date: ")
.append(NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.DAY_OF_MONTH), 2))
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.DAY_OF_MONTH), 2))
.append(".")
.append(NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.MONTH), 2))
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.MONTH), 2))
.append(".")
.append(NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.YEAR), 4))
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.YEAR), 4))
.append(" ")
.append(NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.HOUR_OF_DAY), 2))
.append(de.staropensource.engine.base.utility.Math.padNumbers(Calendar.getInstance().get(Calendar.HOUR_OF_DAY), 2))
.append(":")
.append(NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.MINUTE), 2))
.append(de.staropensource.engine.base.utility.Math.padNumbers(Calendar.getInstance().get(Calendar.MINUTE), 2))
.append(":")
.append(NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.SECOND), 2))
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.SECOND), 2))
.append(" [")
.append(TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT, Locale.US))
.append("]")
.append("\nUNIX Epoch: ")
.append(NumberUtil.padNumbers(System.currentTimeMillis(), String.valueOf(Long.MAX_VALUE).length()))
.append(Math.padNumbers(System.currentTimeMillis(), String.valueOf(Long.MAX_VALUE).length()))
.append("\nOperating system: ")
.append(System.getProperty("os.name"))
@ -293,7 +293,7 @@ public final class CrashHandler {
.append(thread.isDaemon())
.append("):")
.append("\n")
.append(Miscellaneous.stacktraceAsString(stacktraces.get(thread), false))
.append(Miscellaneous.getStackTraceAsString(stacktraces.get(thread), false))
.append("\n");
}
output.append("\n");

View file

@ -24,7 +24,7 @@ import de.staropensource.engine.base.implementation.shortcode.EmptyShortcodePars
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.misc.NumberUtil;
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;
@ -213,11 +213,11 @@ public final class Processor {
private static void time(@NotNull StringBuilder builder) {
if (isFeatureEnabled("time"))
builder
.append(NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.HOUR_OF_DAY), 2))
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.HOUR_OF_DAY), 2))
.append(":")
.append(NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.MINUTE), 2))
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.MINUTE), 2))
.append(":")
.append(NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.SECOND), 2));
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.SECOND), 2));
}
/**
@ -229,11 +229,11 @@ public final class Processor {
private static void date(@NotNull StringBuilder builder) {
if (isFeatureEnabled("date"))
builder
.append(NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.DAY_OF_MONTH), 2))
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.DAY_OF_MONTH), 2))
.append(".")
.append(NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.MONTH), 2))
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.MONTH), 2))
.append(".")
.append(NumberUtil.padNumbers(Calendar.getInstance().get(Calendar.YEAR), 4));
.append(Math.padNumbers(Calendar.getInstance().get(Calendar.YEAR), 4));
}
/**

View file

@ -21,6 +21,7 @@ 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;
@ -66,8 +67,8 @@ public final class LoggingQueue {
* @since v1-alpha8
*/
public static void flush() {
// Get copy of and clear the queue
List<@NotNull QueuedLogCall> queue = new ArrayList<>(LoggingQueue.queue);
// Get copy of and clear queue
List<@NotNull QueuedLogCall> queue = new ImmutableArrayList<>(LoggingQueue.queue);
LoggingQueue.queue.clear();
for (QueuedLogCall queuedCall : queue)

View file

@ -20,7 +20,7 @@
package de.staropensource.engine.base.type;
import de.staropensource.engine.base.EngineConfiguration;
import de.staropensource.engine.base.utility.misc.NumberUtil;
import de.staropensource.engine.base.utility.Math;
import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;
@ -42,7 +42,7 @@ import java.util.Locale;
@Getter
@Setter
@SuppressWarnings({ "JavadocDeclaration" })
public class Color {
public final class Color {
/**
* Contains the red color value.
*
@ -118,10 +118,10 @@ public class Color {
* @since v1-alpha6
*/
private Color(int red, int green, int blue, int alpha) {
this.red = NumberUtil.limitNumber(0, 255, red);
this.green = NumberUtil.limitNumber(0, 255, green);
this.blue = NumberUtil.limitNumber(0, 255, blue);
this.alpha = NumberUtil.limitNumber(0, 255, alpha);
this.red = Math.boundNumber(0, 255, red);
this.green = Math.boundNumber(0, 255, green);
this.blue = Math.boundNumber(0, 255, blue);
this.alpha = Math.boundNumber(0, 255, alpha);
}
/**

View file

@ -0,0 +1,81 @@
/*
* 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.type;
import de.staropensource.engine.base.annotation.EventListener;
/**
* Allows an {@link EventListener} method to specify when it should be invoked.
*
* @since v1-alpha0
*/
@SuppressWarnings({ "unused" })
public enum EventPriority {
/**
* {@link EventListener}s with this priority are processed first, even before {@link #VERY_IMPORTANT}.
* This event is exclusive to subsystems and should not be used by applications.
*
* @since v1-alpha0
*/
EXCLUSIVELY_IMPORTANT,
/**
* {@link EventListener}s with this priority are invoked first.
*
* @since v1-alpha0
*/
VERY_IMPORTANT,
/**
* {@link EventListener}s with this priority are invoked second.
*
* @since v1-alpha0
*/
IMPORTANT,
/**
* The default event priority, {@link EventListener}s with this priority are invoked third.
*
* @since v1-alpha0
*/
DEFAULT,
/**
* {@link EventListener}s with this priority are invoked fourth.
*
* @since v1-alpha0
*/
UNIMPORTANT,
/**
* {@link EventListener}s with this priority are invoked last.
*
* @since v1-alpha0
*/
VERY_UNIMPORTANT,
/**
* {@link EventListener}s with this priority are invoked last, even after {@link #VERY_UNIMPORTANT}.
* This event is exclusive to subsystems and should not be used by applications.
*
* @since v1-alpha0
*/
EXCLUSIVELY_UNIMPORTANT
}

View file

@ -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.type;
/**
* Represents various file types.
*
* @since v1-alpha8
*/
public enum FileType {
/**
* The path does not exist.
*
* @since v1-alpha8
*/
VOID,
/**
* It's a regular file.
*
* @since v1-alpha8
*/
FILE,
/**
* It's a directory containing files.
*
* @since v1-alpha8
*/
DIRECTORY,
/**
* The file type is unknown to the sos!engine.
*
* @since v1-alpha8
*/
UNKNOWN
}

View file

@ -29,32 +29,32 @@ import org.jetbrains.annotations.NotNull;
*/
public enum Tristate {
/**
* Defines an unset state.
* An unset tristate.
*
* @since v1-alpha1
*/
UNSET,
/**
* Defines a true state.
* A true tristate.
*
* @since v1-alpha1
*/
TRUE,
/**
* Defines a false state.
* A false tristate.
*
* @since v1-alpha1
*/
FALSE;
/**
* Converts the specified {@link Boolean} into a {@link Tristate}.
* Converts the {@link Boolean} into a {@link Tristate}.
*
* @param bool boolean to convert
* @return {@link Tristate} representation of the specified boolean
* @since v1-alpha9
* @return tristated boolean
* @since v1-alpha5
*/
public static @NotNull Tristate toTristate(boolean bool) {
if (bool) return Tristate.TRUE;
@ -62,28 +62,12 @@ public enum Tristate {
}
/**
* Converts the specified {@link Integer} into a {@link Tristate}.
*
* @param integer integer to convert
* @return {@link Tristate} representation of the specified integer
* @since v1-alpha9
*/
public static @NotNull Tristate toTristate(int integer) {
return switch (integer) {
case 0 -> Tristate.FALSE;
case 1 -> Tristate.TRUE;
case 2 -> Tristate.UNSET;
default -> throw new IndexOutOfBoundsException("Supplied integer '" + integer + "' is not in range 0-2");
};
}
/**
* Converts this {@link Tristate} into a {@link Boolean}.
* Converts the {@link Tristate} into a {@link Boolean}.
* Make sure to check for {@link #UNSET} first.
*
* @return trimmed boolean representation of this {@link Tristate}
* @throws TristateConversionException on encountering {@link #UNSET}
* @since v1-alpha9
* @return booleanized {@link Tristate}
* @throws TristateConversionException when encountering {@link #UNSET}
* @since v1-alpha2
*/
public boolean toBoolean() {
return switch (this) {
@ -92,19 +76,4 @@ public enum Tristate {
case FALSE -> false;
};
}
/**
* Converts this {@link Tristate} into an {@link Integer}.
*
* @return integer representation of this {@link Tristate}
* @throws TristateConversionException when encountering {@link #UNSET}
* @since v1-alpha9
*/
public int toInteger() {
return switch (this) {
case FALSE -> 0;
case TRUE -> 1;
case UNSET -> 2;
};
}
}

View file

@ -22,8 +22,7 @@ package de.staropensource.engine.base.type;
import de.staropensource.engine.base.implementation.versioning.StarOpenSourceVersioningSystem;
/**
* Provides all available version types as specified
* by the StarOpenSource Versioning System v2.
* Provides all available version types specified in the StarOpenSource Versioning System v2.
*
* @see StarOpenSourceVersioningSystem
* @since v1-alpha0

View file

@ -0,0 +1,222 @@
/*
* 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.type.immutable;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.UnaryOperator;
/**
* An unmodifiable {@link ArrayList}.
*
* @param <E> contained type
* @since v1-alpha1
*/
@SuppressWarnings({ "unused" })
public class ImmutableArrayList<E> extends ArrayList<E> {
/**
* Creates and initializes an instance of this class.
*
* @since v1-alpha1
*/
public ImmutableArrayList() {}
/**
* Creates and initializes an instance of this class.
* Converts a {@link LinkedList} into an {@link ImmutableArrayList}.
*
* @param list {@link List} to convert
* @since v1-alpha1
*/
public ImmutableArrayList(@NotNull List<E> list) {
super.addAll(list);
}
/**
* Converts the {@link ImmutableArrayList} into a regular {@link ArrayList}.
*
* @return mutable {@link ArrayList}
* @since v1-alpha2
*/
public @NotNull ArrayList<E> toMutable() {
return new ArrayList<>(this);
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public boolean add(E e) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public boolean addAll(@NotNull Collection<? extends E> c) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public boolean addAll(int index, @NotNull Collection<? extends E> c) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public boolean removeAll(@NotNull Collection<?> c) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public boolean retainAll(@NotNull Collection<?> c) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public void replaceAll(UnaryOperator<E> operator) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public void sort(Comparator<? super E> c) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public void clear() {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public E set(int index, E element) throws UnsupportedOperationException {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public void add(int index, E element) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public E remove(int index) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public void addFirst(E e) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public void addLast(E e) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public E removeFirst() {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public E removeLast() {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableArrayList");
}
}

View file

@ -0,0 +1,167 @@
/*
* 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.type.immutable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
/**
* An unmodifiable {@link HashMap}.
*
* @param <K> contained key type
* @param <V> contained value type
* @since v1-alpha1
*/
@SuppressWarnings({ "unused" })
public class ImmutableHashMap<K, V> extends HashMap<K, V> {
/**
* Creates and initializes an instance of this class.
*
* @since v1-alpha1
*/
public ImmutableHashMap() {}
/**
* Creates and initializes an instance of this class.
* Converts a {@link Map} into an {@link ImmutableHashMap}.
*
* @param map {@link Map} to convert
* @since v1-alpha1
*/
public ImmutableHashMap(@NotNull Map<K, V> map) {
for (K key : map.keySet())
super.put(key, map.get(key));
}
/**
* Converts the {@link ImmutableHashMap} into a regular {@link HashMap}.
*
* @return mutable {@link HashMap}
* @since v1-alpha2
*/
public @NotNull HashMap<K, V> toMutable() {
return new HashMap<>(this);
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, hash map is immutable
*/
@Override
public @Nullable V put(K key, V value) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, hash map is immutable
*/
@Override
public @Nullable V remove(Object key) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, hash map is immutable
*/
@Override
public void putAll(@NotNull Map<? extends K, ? extends V> m) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, hash map is immutable
*/
@Override
public void clear() {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, hash map is immutable
*/
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, hash map is immutable
*/
@Override
public @Nullable V putIfAbsent(K key, V value) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, hash map is immutable
*/
@Override
public boolean remove(Object key, Object value) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, hash map is immutable
*/
@Override
public boolean replace(K key, V oldValue, V newValue) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, hash map is immutable
*/
@Override
public @Nullable V replace(K key, V value) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, hash map is immutable
*/
@Override
public @Nullable V merge(K key, @NotNull V value, @NotNull BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashMap");
}
}

View file

@ -0,0 +1,134 @@
/*
* 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.type.immutable;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
/**
* An unmodifiable {@link HashSet}.
*
* @param <E> contained type
* @since v1-alpha2
*/
@SuppressWarnings({ "unused" })
public class ImmutableHashSet<E> extends HashSet<E> {
/**
* Creates and initializes an instance of this class.
*
* @since v1-alpha2
*/
public ImmutableHashSet() {}
/**
* Creates and initializes an instance of this class.
* Converts a {@link Set} into an {@link ImmutableHashSet}.
*
* @param set {@link Set} to convert
* @since v1-alpha2
*/
public ImmutableHashSet(@NotNull Set<E> set) {
super.addAll(set);
}
/**
* Converts the {@link ImmutableHashSet} into a regular {@link HashSet}.
*
* @return mutable {@link HashSet}
* @since v1-alpha2
*/
public @NotNull HashSet<E> toMutable() {
return new HashSet<>(this);
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public boolean add(Object o) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashSet");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashSet");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public boolean addAll(@NotNull Collection collection) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashSet");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public boolean retainAll(@NotNull Collection collection) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashSet");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public boolean removeIf(Predicate filter) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashSet");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public void clear() {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashSet");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, array list is immutable
*/
@Override
public boolean removeAll(@NotNull Collection collection) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableHashSet");
}
}

View file

@ -0,0 +1,167 @@
/*
* 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.type.immutable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiFunction;
/**
* An unmodifiable {@link Map}.
*
* @param <K> contained key type
* @param <V> contained value type
* @since v1-alpha1
*/
@SuppressWarnings({ "unused" })
public class ImmutableLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
/**
* Creates and initializes an instance of this class.
*
* @since v1-alpha1
*/
public ImmutableLinkedHashMap() {}
/**
* Creates and initializes an instance of this class.
* Converts a {@link Map} into an {@link ImmutableLinkedHashMap}.
*
* @param map {@link Map} to convert
* @since v1-alpha1
*/
public ImmutableLinkedHashMap(@NotNull Map<K, V> map) {
for (K key : map.keySet())
super.put(key, map.get(key));
}
/**
* Converts the {@link ImmutableLinkedHashMap} into a regular {@link LinkedHashMap}.
*
* @return mutable {@link LinkedHashMap}
* @since v1-alpha2
*/
public @NotNull LinkedHashMap<K, V> toMutable() {
return new LinkedHashMap<>(this);
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked hash map is immutable
*/
@Override
public @Nullable V put(K key, V value) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked hash map is immutable
*/
@Override
public @Nullable V remove(Object key) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked hash map is immutable
*/
@Override
public void putAll(@NotNull Map<? extends K, ? extends V> m) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked hash map is immutable
*/
@Override
public void clear() {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked hash map is immutable
*/
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked hash map is immutable
*/
@Override
public @Nullable V putIfAbsent(K key, V value) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked hash map is immutable
*/
@Override
public boolean remove(Object key, Object value) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked hash map is immutable
*/
@Override
public boolean replace(K key, V oldValue, V newValue) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked hash map is immutable
*/
@Override
public @Nullable V replace(K key, V value) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedHashMap");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked hash map is immutable
*/
@Override
public @Nullable V merge(K key, @NotNull V value, @NotNull BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedHashMap");
}
}

View file

@ -0,0 +1,222 @@
/*
* 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.type.immutable;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.function.UnaryOperator;
/**
* An unmodifiable {@link LinkedList}.
*
* @param <E> contained type
* @since v1-alpha1
*/
@SuppressWarnings({ "unused" })
public class ImmutableLinkedList<E> extends LinkedList<E> {
/**
* Creates and initializes an instance of this class.
*
* @since v1-alpha1
*/
public ImmutableLinkedList() {}
/**
* Creates and initializes an instance of this class.
* Converts a {@link LinkedList} into an {@link ImmutableLinkedList}.
*
* @param list {@link List} to convert
* @since v1-alpha1
*/
public ImmutableLinkedList(@NotNull List<E> list) {
super.addAll(size(), list);
}
/**
* Converts the {@link ImmutableLinkedList} into a regular {@link LinkedList}.
*
* @return mutable {@link LinkedList}
* @since v1-alpha2
*/
public @NotNull LinkedList<E> toMutable() {
return new LinkedList<>(this);
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public boolean add(E e) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public boolean addAll(@NotNull Collection<? extends E> c) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public boolean addAll(int index, @NotNull Collection<? extends E> c) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public boolean removeAll(@NotNull Collection<?> c) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public boolean retainAll(@NotNull Collection<?> c) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public void replaceAll(UnaryOperator<E> operator) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public void sort(Comparator<? super E> c) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public void clear() {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public E set(int index, E element) throws UnsupportedOperationException {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public void add(int index, E element) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public E remove(int index) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public void addFirst(E e) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public void addLast(E e) {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public E removeFirst() {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
/**
* {@inheritDoc}
*
* @throws UnsupportedOperationException always, linked list is immutable
*/
@Override
public E removeLast() {
throw new UnsupportedOperationException("This method cannot be executed on an ImmutableLinkedList");
}
}

View file

@ -0,0 +1,25 @@
/*
* 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/>.
*/
/**
* Immutable variations of regular Java data types.
*
* @since v1-alpha1
*/
package de.staropensource.engine.base.type.immutable;

View file

@ -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.type.logging;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Defines a rule used during logging to allow or prevent certain log calls.
*
* @since v1-alpha1
*/
@Getter
@SuppressWarnings({ "JavadocDeclaration" })
public abstract class LogRule {
/**
* Contains if the log rule should disallow or permit matching log calls.
*
* @see LogRuleType
* @since v1-alpha1
* -- GETTER --
* Returns if the log rule should disallow or permit matching log calls.
*
* @return rule type
* @see LogRuleType
* @since v1-alpha1
*/
private final @NotNull LogRuleType type;
/**
* Creates and initializes an instance of this abstract class.
*
* @param type rule type
*/
public LogRule(@NotNull LogRuleType type) {
this.type = type;
}
/**
* Evaluates the rule and returns if the rule matches.
*
* @param level level
* @param issuerClass class of the issuer
* @param issuerOrigin origin of the issuer
* @param issuerMetadata metadata about the issuer
* @param message raw message
* @return if the rule matches
*/
public abstract boolean evaluate(@NotNull LogLevel level, @NotNull Class<?> issuerClass, @NotNull String issuerOrigin, @Nullable String issuerMetadata, @NotNull String message);
}

View file

@ -0,0 +1,43 @@
/*
* 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.type.logging;
/**
* Used for determining if a {@link LogRule} should allow (whitelist) or prevent (blacklist) log call processing.
*
* @since v1-alpha1
*/
public enum LogRuleType {
/**
* Indicates that log calls matching this rule will always be allowed, even if blacklisted.
*
* @see #BLACKLIST
* @since v1-alpha1
*/
WHITELIST,
/**
* Indicates that log calls matching this will always be disallowed except when whitelisted.
*
* @see #WHITELIST
* @since v1-alpha1
*/
BLACKLIST
}

View file

@ -22,7 +22,7 @@ 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.utility.misc.Miscellaneous;
import de.staropensource.engine.base.type.FileType;
import lombok.Getter;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@ -190,7 +190,7 @@ public final class FileAccess {
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.throwableHeader(exception) + "\n" + Miscellaneous.stacktraceAsString(exception, true));
Logger.error("File or directory \"" + path + "\" could not be deleted\n" + Miscellaneous.getStackTraceHeader(exception) + "\n" + Miscellaneous.getStackTraceAsString(exception, true));
}
}
}
@ -267,15 +267,15 @@ public final class FileAccess {
* @return file type
* @since v1-alpha8
*/
public @NotNull FileAccess.Type getType() {
public @NotNull FileType getType() {
if (!exists())
return Type.VOID;
return FileType.VOID;
else if (Files.isRegularFile(path))
return Type.FILE;
return FileType.FILE;
else if (Files.isDirectory(path))
return Type.DIRECTORY;
return FileType.DIRECTORY;
else
return Type.UNKNOWN;
return FileType.UNKNOWN;
}
/**
@ -309,7 +309,7 @@ public final class FileAccess {
* @since v1-alpha8
*/
public @NotNull String @NotNull [] listContents() throws UnsupportedOperationException, IOException {
if (getType() != Type.DIRECTORY)
if (getType() != FileType.DIRECTORY)
throw new UnsupportedOperationException("The file \"" + path + "\" is not a directory");
String[] list = file.list();
@ -661,7 +661,7 @@ public final class FileAccess {
* Returns the contents of this file.
* <p>
* Returns an empty array if this file
* is not of type {@link Type#FILE}.
* is not of type {@link FileType#FILE}.
*
* @return file contents in bytes
* @throws IOException on an IO error
@ -669,7 +669,7 @@ public final class FileAccess {
* @since v1-alpha8
*/
public byte @NotNull [] readBytes() throws IOException, OutOfMemoryError {
if (getType() != Type.FILE)
if (getType() != FileType.FILE)
return new byte[0];
Logger.diag("Reading file \"" + path + "\" (bytes)");
@ -680,7 +680,7 @@ public final class FileAccess {
* Returns the contents of this file.
* <p>
* Returns an empty list if this file
* is not of type {@link Type#FILE}.
* is not of type {@link FileType#FILE}.
*
* @return file contents in bytes
* @throws IOException on an IO error
@ -688,7 +688,7 @@ public final class FileAccess {
* @since v1-alpha8
*/
public @NotNull List<@NotNull String> readLines() throws IOException, OutOfMemoryError {
if (getType() != Type.FILE)
if (getType() != FileType.FILE)
return new ArrayList<>();
Logger.diag("Reading file \"" + path + "\" (lines)");
@ -701,7 +701,7 @@ public final class FileAccess {
* {@link StandardCharsets#UTF_8} character set.
* <p>
* Returns an empty string if this file
* is not of type {@link Type#FILE}.
* is not of type {@link FileType#FILE}.
*
* @return file contents as a string
* @throws IOException on an IO error
@ -716,7 +716,7 @@ public final class FileAccess {
* Returns the contents of this file.
* <p>
* Returns an empty string if this file
* is not of type {@link Type#FILE}.
* is not of type {@link FileType#FILE}.
*
* @param charset charset to decode the bytes with
* @return file contents as a string
@ -725,7 +725,7 @@ public final class FileAccess {
* @since v1-alpha8
*/
public @NotNull String readContent(@NotNull Charset charset) throws IOException, OutOfMemoryError {
if (getType() != Type.FILE)
if (getType() != FileType.FILE)
return "";
Logger.diag("Reading file \"" + path + "\" (string)");
@ -739,15 +739,15 @@ public final class FileAccess {
*
* @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 Type#VOID} or {@link Type#FILE}
* @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() == Type.VOID)
if (getType() == FileType.VOID)
createFile();
else if (getType() != Type.FILE)
throw new UnsupportedOperationException("File \"" + path + "\" is not of type Type.VOID or Type.FILE");
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") + ")");
@ -762,7 +762,7 @@ public final class FileAccess {
*
* @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 Type#VOID} or {@link Type#FILE}
* @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
*/
@ -776,15 +776,15 @@ public final class FileAccess {
* @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 Type#VOID} or {@link Type#FILE}
* @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() == Type.VOID)
if (getType() == FileType.VOID)
createFile();
else if (getType() != Type.FILE)
throw new UnsupportedOperationException("File \"" + path + "\" is not of type Type.VOID or Type.FILE");
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);
@ -798,15 +798,15 @@ public final class FileAccess {
*
* @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 Type#VOID} or {@link Type#FILE}
* @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() == Type.VOID)
if (getType() == FileType.VOID)
createFile();
else if (getType() != Type.FILE)
throw new UnsupportedOperationException("File \"" + path + "\" is not of type Type.VOID or Type.FILE");
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);
@ -820,7 +820,7 @@ public final class FileAccess {
*
* @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 Type#VOID} or {@link Type#FILE}
* @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
*/
@ -834,53 +834,18 @@ public final class FileAccess {
* @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 Type#VOID} or {@link Type#FILE}
* @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() == Type.VOID)
if (getType() == FileType.VOID)
createFile();
else if (getType() != Type.FILE)
throw new UnsupportedOperationException("File \"" + path + "\" is not of type Type.VOID or Type.FILE");
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;
}
/**
* Represents various file types.
*
* @since v1-alpha8
*/
public enum Type {
/**
* The path does not exist.
*
* @since v1-alpha8
*/
VOID,
/**
* It's a regular file.
*
* @since v1-alpha8
*/
FILE,
/**
* It's a directory containing files.
*
* @since v1-alpha8
*/
DIRECTORY,
/**
* The file type is unknown to the sos!engine.
*
* @since v1-alpha8
*/
UNKNOWN
}
}

View file

@ -17,49 +17,43 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.base.utility.misc;
package de.staropensource.engine.base.utility;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Converts arrays, arrays and maps to look like
* nicely formatted lists (as {@link String}s).
* Converts various data types to {@link String}s.
*
* @since v1-alpha9
* @since v1-alpha4
*/
public final class ListFormatter {
/**
* Creates and initializes an instance of this class.
*
* @since v1-alpha9
* @since v1-alpha6
*/
private ListFormatter() {}
/**
* Converts the specified array to a {@link String}.
* <p>
* For {@code formatArray(array, ", ", " & ")} the
* something like following string will be returned:
* {@code element 1, element 2, element 3 & element 4}
* Converts an array to a {@link String}.
*
* @param array array to convert
* @param itemSeparator string used to separate two items
* @param finalSeparator string used to separate the final two items
* @param array array to convert
* @return formatted string
* @since v1-alpha9
* @since v1-alpha4
*/
public static @NotNull String formatArray(@NotNull Object @NotNull [] array, @NotNull String itemSeparator, @NotNull String finalSeparator) {
public static @NotNull String formatArray(@NotNull Object @NotNull [] array) {
StringBuilder output = new StringBuilder();
for (int index = 0; index < array.length; index++) {
if (!output.isEmpty())
if (index == array.length - 1)
output.append(finalSeparator);
output.append(" & ");
else
output.append(itemSeparator);
output.append(", ");
output.append(array[index]);
}
@ -68,50 +62,25 @@ public final class ListFormatter {
}
/**
* Converts the specified array to a {@link String}.
* <p>
* This will produce something like this:
* {@code element 1, element 2, element 3 & element 4}
* Converts a {@link Set} to a {@link String}.
*
* @param array array to convert
* @param set set to convert
* @return formatted string
* @since v1-alpha9
* @since v1-alpha4
*/
public static @NotNull String formatArray(@NotNull Object @NotNull [] array) {
return formatArray(array, ", ", " & ");
public static @NotNull String formatSet(@NotNull Set<?> set) {
return formatArray(set.toArray());
}
/**
/**
* Converts the specified array to a {@link String}.
* <p>
* For {@code formatArray(array, ", ", " & ")} the
* something like following string will be returned:
* {@code element 1, element 2, element 3 & element 4}
* Converts a {@link List} to a {@link String}.
*
* @param collection collection to convert
* @param itemSeparator string used to separate two items
* @param finalSeparator string used to separate the final two items
* @param list list to convert
* @return formatted string
* @since v1-alpha9
* @since v1-alpha4
*/
public static @NotNull String formatCollection(@NotNull Collection<?> collection, @NotNull String itemSeparator, @NotNull String finalSeparator) {
return formatArray(collection.toArray(), itemSeparator, finalSeparator);
}
/**
* Converts the specified {@link Collection} to a {@link String}.
* <p>
* This will produce something like this:
* {@code element 1, element 2, element 3 & element 4}
*
* @param collection collection to convert
* @return formatted string
* @since v1-alpha9
*/
public static @NotNull String formatCollection(@NotNull Collection<?> collection) {
return formatArray(collection.toArray());
public static @NotNull String formatList(@NotNull List<?> list) {
return formatArray(list.toArray());
}
/**
@ -119,7 +88,7 @@ public final class ListFormatter {
*
* @param map map to convert
* @return formatted string
* @since v1-alpha9
* @since v1-alpha4
*/
public static @NotNull String formatMap(@NotNull Map<?, ?> map) {
StringBuilder output = new StringBuilder();

View file

@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.base.utility.misc;
package de.staropensource.engine.base.utility;
import org.jetbrains.annotations.NotNull;
@ -27,36 +27,31 @@ import java.util.LinkedList;
/**
* Utility class for math operations.
*
* @since v1-alpha9
* @since v1-alpha2
*/
// All mean and double methods have been sourced
// from https://stackoverflow.com/a/4191729, tysm!
@SuppressWarnings({ "unused" })
public final class NumberUtil {
public final class Math {
/**
* Creates and initializes an instance of this class
*
* @since v1-alpha9
* @since v1-alpha6
*/
private NumberUtil() {}
private Math() {}
/**
* Adds padding zeros to a number.
* <p>
* Example:
* {@code padNumbers(505L, 5) = "00505"}
*
* @param number number
* @param length length
* @return the padded number
* @since v1-alpha9
* @since v1-alpha2
*/
public static @NotNull String padNumbers(long number, int length) {
return String.format("%0" + length + "d", number);
}
// -----> Number limiting
/**
* Ensures the integer is inside the
* specified bounds. If not, the
@ -66,10 +61,10 @@ public final class NumberUtil {
* @param max maximum value
* @param value value
* @return number in threshold
* @throws IndexOutOfBoundsException if the minimum value is bigger than the maximum value
* @since v1-alpha9
* @throws IndexOutOfBoundsException when the minimum value is bigger than the maximum value
* @since v1-alpha6
*/
public static int limitNumber(int min, int max, int value) throws IndexOutOfBoundsException {
public static int boundNumber(int min, int max, int value) throws IndexOutOfBoundsException {
if (min > max)
throw new IndexOutOfBoundsException();
@ -90,10 +85,10 @@ public final class NumberUtil {
* @param max maximum value
* @param value value
* @return number in threshold
* @throws IndexOutOfBoundsException if the minimum value is bigger than the maximum value
* @since v1-alpha9
* @throws IndexOutOfBoundsException when the minimum value is bigger than the maximum value
* @since v1-alpha6
*/
public static float limitNumber(float min, float max, float value) throws IndexOutOfBoundsException {
public static float boundNumber(float min, float max, float value) throws IndexOutOfBoundsException {
if (min > max)
throw new IndexOutOfBoundsException();
@ -114,10 +109,10 @@ public final class NumberUtil {
* @param max maximum value
* @param value value
* @return number in threshold
* @throws IndexOutOfBoundsException if the minimum value is bigger than the maximum value
* @since v1-alpha9
* @throws IndexOutOfBoundsException when the minimum value is bigger than the maximum value
* @since v1-alpha6
*/
public static double limitNumber(double min, double max, double value) throws IndexOutOfBoundsException {
public static double boundNumber(double min, double max, double value) throws IndexOutOfBoundsException {
if (min > max)
throw new IndexOutOfBoundsException();
@ -129,16 +124,14 @@ public final class NumberUtil {
return value;
}
// -----> Means and medians
/**
* Returns the mean of a collection of numbers.
*
* @param collection collection of {@code double}s
* @return mean
* @since v1-alpha9
* @since v1-alpha2
*/
public static double calculateMeanDouble(@NotNull Collection<@NotNull Double> collection) {
public static double getMeanDouble(@NotNull Collection<@NotNull Double> collection) {
double sum = 0;
for (double number : collection)
@ -152,9 +145,9 @@ public final class NumberUtil {
*
* @param collection collection of {@code float}s
* @return mean
* @since v1-alpha9
* @since v1-alpha2
*/
public static double calculateMeanFloat(@NotNull Collection<@NotNull Float> collection) {
public static double getMeanFloat(@NotNull Collection<@NotNull Float> collection) {
double sum = 0;
for (float number : collection)
@ -168,9 +161,9 @@ public final class NumberUtil {
*
* @param collection collection of {@code long}s
* @return mean
* @since v1-alpha9
* @since v1-alpha2
*/
public static double calculateMeanLong(@NotNull Collection<@NotNull Long> collection) {
public static double getMeanLong(@NotNull Collection<@NotNull Long> collection) {
double sum = 0;
for (long number : collection)
@ -184,9 +177,9 @@ public final class NumberUtil {
*
* @param collection collection of {@code int}s
* @return mean
* @since v1-alpha9
* @since v1-alpha2
*/
public static double calculateMeanInt(@NotNull Collection<@NotNull Integer> collection) {
public static double getMeanInt(@NotNull Collection<@NotNull Integer> collection) {
double sum = 0;
for (int number : collection)
@ -200,9 +193,9 @@ public final class NumberUtil {
*
* @param list linked list of {@code double}s
* @return median
* @since v1-alpha9
* @since v1-alpha2
*/
public static double calculateMedianDouble(@NotNull LinkedList<@NotNull Double> list) {
public static double getMedianDouble(@NotNull LinkedList<@NotNull Double> list) {
int middle = list.size() / 2;
if (list.size() % 2 == 1)
return list.get(middle);
@ -215,9 +208,9 @@ public final class NumberUtil {
*
* @param list linked list of {@code float}s
* @return median
* @since v1-alpha9
* @since v1-alpha2
*/
public static double calculateMedianFloat(@NotNull LinkedList<@NotNull Float> list) {
public static double getMedianFloat(@NotNull LinkedList<@NotNull Float> list) {
int middle = list.size() / 2;
if (list.size() % 2 == 1)
return list.get(middle);
@ -230,9 +223,9 @@ public final class NumberUtil {
*
* @param list linked list of {@code long}s
* @return median
* @since v1-alpha9
* @since v1-alpha2
*/
public static double calculateMedianLong(@NotNull LinkedList<@NotNull Long> list) {
public static double getMedianLong(@NotNull LinkedList<@NotNull Long> list) {
int middle = list.size() / 2;
if (list.size() % 2 == 1)
return list.get(middle);
@ -245,9 +238,9 @@ public final class NumberUtil {
*
* @param list linked list of {@code int}s
* @return median
* @since v1-alpha9
* @since v1-alpha2
*/
public static double calculateMedianInt(@NotNull LinkedList<@NotNull Integer> list) {
public static double getMedianInt(@NotNull LinkedList<@NotNull Integer> list) {
int middle = list.size() / 2;
if (list.size() % 2 == 1)
return list.get(middle);

View file

@ -0,0 +1,254 @@
/*
* 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.event.ThrowableCatchEvent;
import de.staropensource.engine.base.exception.TristateConversionException;
import de.staropensource.engine.base.type.Tristate;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.stream.Collectors;
/**
* Contains smaller functions that don't fit into other utility classes.
*
* @since v1-alpha0
*/
@SuppressWarnings({ "unused" })
public final class Miscellaneous {
/**
* Creates and initializes an instance of this class.
*
* @since v1-alpha6
*/
private Miscellaneous() {}
/**
* Converts a boolean into an integer.
*
* @param bool boolean to convert
* @return converted integer
* @since v1-alpha2
*/
@Range(from = 0, to = 1)
public static int getIntegerizedBoolean(boolean bool) {
return bool ? 1 : 0;
}
/**
* Converts an integer into a {@link Tristate}.
*
* @param integer integer to convert
* @return expected boolean result, except if neither {@code 0} or {@code 1}, in which case {@link Tristate#UNSET} is returned
* @since v1-alpha2
*/
public static Tristate getTristatedInteger(@Range(from = 0, to = 1) int integer) {
return switch (integer) {
case 0 -> Tristate.TRUE;
case 1 -> Tristate.FALSE;
default -> Tristate.UNSET;
};
}
/**
* Converts an integer into a {@link Tristate} and then into a boolean.
*
* @param integer integer to convert
* @return booleanized integer
* @throws TristateConversionException when encountering {@link Tristate#UNSET}.
* @since v1-alpha2
*/
public static boolean getBooleanizedInteger(@Range(from = 0, to = 1) int integer) throws TristateConversionException {
return getTristatedInteger(integer).toBoolean();
}
/**
* Searches for a value in a {@link Map}.
*
* @param map map to use
* @param value value to search for
* @return all keys matching the specified value
* @since v1-alpha0
*/
public static Set<?> getMapValues(@NotNull Map<?, ?> map, @Nullable Object value) {
return map
.entrySet().stream()
.filter(entry -> Objects.equals(entry.getValue(), value))
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
}
/**
* Counts the occurrences of a substring inside of a string.
*
* @param string string to search
* @param substring substring to search for
* @return occurrences
*/
public static long countOccurrences(@NotNull String string, @NotNull String substring) {
return (string.length() - string.replace(substring, "").length()) / substring.length();
}
/**
* Returns the correct separator to use when splitting a string.
*
* @param string string to split
* @param separators separators to check
* @param requiredOccurrences exact amount of occurrences for a separator to be deemed valid
* @return separator to use or {@code null}
* @since v1-alpha1
*/
public static @Nullable String getSeparator(@NotNull String string, @NotNull String @NotNull [] separators, int requiredOccurrences) {
if (string.isBlank() || separators.length == 0 || requiredOccurrences == 0)
return null;
for (String separator : separators)
if (countOccurrences(string, separator) == requiredOccurrences)
return separator;
return null;
}
/**
* Returns the correct separator to use when splitting a string.
*
* @param string string to split
* @param separators separators to check
* @param minimumOccurrences minimum amount of occurrences for a separator to be deemed valid
* @return separator to use or {@code null}
* @since v1-alpha1
*/
public static @Nullable String getSeparator(@NotNull String string, List<String> separators, Integer minimumOccurrences) {
for (String separator : separators)
if (countOccurrences(string, separator) >= minimumOccurrences)
return separator;
return null;
}
/**
* Measures the execution time of a {@link Runnable}.
*
* @param runnable {@link Runnable} to execute
* @return execution time in milliseconds
* @see Runnable
* @since v1-alpha0
*/
public static long measureExecutionTime(@NotNull Runnable runnable) {
long initTime = System.currentTimeMillis();
runnable.run();
return System.currentTimeMillis() - initTime;
}
/**
* Executes a {@link Runnable} and emits {@link ThrowableCatchEvent} if a throwable is caught.
*
* @param runnable {@link Runnable} to execute
* @param identifier some identifier to distinguish {@link Runnable}s
* @since v1-alpha1
*/
public static void executeSafely(@NotNull Runnable runnable, @NotNull String identifier) {
try {
runnable.run();
} catch (Throwable throwable) {
new ThrowableCatchEvent().callEvent(throwable, identifier);
}
}
/**
* Forcefully invokes the garbage collector
* and blocks execution until finished.
* If you want to run it in parallel to your
* program, consider running it in a {@code VirtualThread}.
* <p>
* This method does not guarantee full garbage collection,
* as the JVM only hints the garbage collector to do it's job.
* This is because all garbage collectors are non-deterministic.
*
* @since v1-alpha0
*/
@SuppressWarnings("UnusedAssignment")
public static void invokeGarbageCollector() {
Object object = new Object();
WeakReference<Object> weakReference = new WeakReference<>(object);
object = null;
while(weakReference.get() != null) System.gc();
}
/**
* Ensures that the code is running on the main thread.
*
* @return {@code true} if running on the main thread, {@code false} otherwise
* @since v1-alpha2
*/
public static boolean onMainThread() {
return Thread.currentThread().threadId() == 1;
}
/**
* Returns the {@code Caused by} message usually found when the JVM prints a throwable.
*
* @param throwable {@link Throwable} to use
* @return stack trace header
* @since v1-alpha4
*/
public static @NotNull String getStackTraceHeader(@NotNull Throwable throwable) {
return "Caused by: " + throwable.getClass().getName() + (throwable.getMessage() == null ? "" : ": " + throwable.getMessage());
}
/**
* Converts an array of {@link StackTraceElement}s into a regular string.
*
* @param stacktrace array of {@link StackTraceElement}s to convert
* @param indent if all lines should be indented with a single {@code \t} character, like in regular stack traces
* @return converted stacktrace string
* @since v1-alpha4
*/
public static @NotNull String getStackTraceAsString(@NotNull StackTraceElement @NotNull [] stacktrace, boolean indent) {
StringBuilder output = new StringBuilder();
for (StackTraceElement element : stacktrace) {
if (!output.isEmpty())
output.append("\n");
if (indent)
output.append("\t");
output.append("at ").append(element);
}
return output.toString();
}
/**
* Converts the stacktrace of a throwable into a regular string.
*
* @param throwable throwable to derive the stacktrace from
* @param indent if all lines should be indented with a single {@code \t} character, like in regular stack traces
* @return converted stacktrace string
* @since v1-alpha4
*/
public static @NotNull String getStackTraceAsString(@NotNull Throwable throwable, boolean indent) {
return getStackTraceAsString(throwable.getStackTrace(), indent);
}
}

View file

@ -20,11 +20,11 @@
package de.staropensource.engine.base.utility.information;
import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.type.immutable.ImmutableArrayList;
import org.jetbrains.annotations.NotNull;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.util.Collections;
import java.util.List;
/**
@ -114,7 +114,7 @@ public final class JvmInformation {
* @since v1-alpha0
*/
public static @NotNull List<@NotNull String> getArguments() {
return Collections.unmodifiableList(ManagementFactory.getRuntimeMXBean().getInputArguments());
return new ImmutableArrayList<>(ManagementFactory.getRuntimeMXBean().getInputArguments());
}
/**

View file

@ -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.utility.misc;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.Collections;
/**
* Manipulates arrays.
*
* @since v1-alpha9
*/
public final class ArrayUtil {
/**
* Removes the first {@code n} elements
* from the specified array.
* <p>
* If {@code n} is bigger than the
* amount of elements in the
* specified array, an empty array
* will be returned.
*
* @param array array to operate on
* @param n amount to remove
* @return array with the first elements removed
* @since v1-alpha9
*/
public static <T> @Nullable T @NotNull [] removeFirst(@Nullable T @NotNull [] array, int n) {
try {
return Arrays.copyOfRange(array, n, array.length);
} catch (ArrayIndexOutOfBoundsException | IllegalArgumentException exception) {
return Collections.emptyList().toArray(array);
}
}
/**
* Removes the last {@code n} elements
* from the specified array.
* <p>
* If {@code n} is bigger than the
* amount of elements in the
* specified array, an empty array
* will be returned.
*
* @param array array to operate on
* @param n amount to remove
* @return array with the last elements removed
* @since v1-alpha9
*/
public static <T> @Nullable T @NotNull [] removeLast(@Nullable T @NotNull [] array, int n) {
try {
return Arrays.copyOfRange(array, 0, array.length-n);
} catch (ArrayIndexOutOfBoundsException | IllegalArgumentException exception) {
return Collections.emptyList().toArray(array);
}
}
}

View file

@ -1,211 +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.misc;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.stream.Collectors;
/**
* Contains the most miscellaneous of all miscellaneous methods.
*
* @since v1-alpha9
*/
@SuppressWarnings({ "unused" })
public final class Miscellaneous {
/**
* Creates and initializes an instance of this class.
*
* @since v1-alpha9
*/
private Miscellaneous() {}
/**
* Searches for a value in a {@link Map}.
*
* @param map map to use
* @param value value to search for
* @return all keys matching the specified value
* @since v1-alpha9
*/
public static Set<?> searchForValueInMap(@NotNull Map<?, ?> map, @Nullable Object value) {
return map
.entrySet().stream()
.filter(entry -> Objects.equals(entry.getValue(), value))
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
}
/**
* Measures the execution time of
* the specified {@link Runnable}.
*
* @param runnable {@link Runnable} to execute
* @return execution time in milliseconds
* @see Runnable
* @since v1-alpha9
*/
public static long measureExecutionTime(@NotNull Runnable runnable) {
long initTime = System.currentTimeMillis();
runnable.run();
return System.currentTimeMillis() - initTime;
}
/**
* Forcefully invokes the garbage collector
* and blocks execution until finished.
* If you want to run it in parallel to your
* program, consider running it in a thread.
* <p>
* This method does not guarantee full garbage collection,
* as the JVM only hints the garbage collector to do it's
* job. All garbage collectors are non-deterministic and
* cannot be invoked by force. And even if this method
* manages to invoke the garbage collector, a full garbage
* collection likely will never be archived. This is because
* the garbage collector will never throw everything into
* the trash. In addition, this method will only return if
* it's {@link WeakReference} becomes null. The garbage
* collector may or may not do more afterwards. This method
* just exists if someone wants to at least try to
* forcefully invoke the garbage collector. If you want
* a somewhat more reliable method, use Java Agents.
*
* @since v1-alpha9
*/
@SuppressWarnings("UnusedAssignment")
public static void invokeGarbageCollector() {
Object object = new Object();
WeakReference<Object> weakReference = new WeakReference<>(object);
object = null;
while(weakReference.get() != null) System.gc();
}
/**
* Returns if the code it is invoked by
* is running on the Java main thread.
*
* @return running on main thread?
* @since v1-alpha9
*/
public static boolean onMainThread() {
return Thread.currentThread().threadId() == 1;
}
/**
* Returns the {@code Caused by} message
* usually found in JVM stack traces.
*
* @param throwable {@link Throwable} to use
* @return {@link Throwable} header
* @since v1-alpha9
*/
public static @NotNull String throwableHeader(@NotNull Throwable throwable) {
return "Caused by: " + throwable.getClass().getName() + (throwable.getMessage() == null ? "" : ": " + throwable.getMessage());
}
/**
* Converts an array of {@link StackTraceElement}s into a regular string.
*
* @param stacktrace array of {@link StackTraceElement}s to convert
* @param indent if all lines shall be indented with a single tab.
* Enabling this will cause the output of this method
* to mimic the JVM's throwable handling output.
* @return specified stack trace as a String
* @since v1-alpha9
*/
public static @NotNull String stacktraceAsString(@NotNull StackTraceElement @NotNull [] stacktrace, boolean indent) {
StringBuilder output = new StringBuilder();
for (StackTraceElement element : stacktrace) {
if (!output.isEmpty())
output.append("\n");
if (indent)
output.append("\t");
output.append("at ").append(element);
}
return output.toString();
}
/**
* Converts an array of {@link StackTraceElement}s into a regular string.
*
* @param throwable throwable to derive the stacktrace from
* @param indent if all lines shall be indented with a single tab.
* Enabling this will cause the output of this method
* to mimic the JVM's throwable handling output.
* @return specified stack trace as a String
* @since v1-alpha9
*/
public static @NotNull String stacktraceAsString(@NotNull Throwable throwable, boolean indent) {
return stacktraceAsString(throwable.getStackTrace(), indent);
}
/**
* Returns the full stack trace of a {@link Throwable}.
* <p>
* This method recursively resolves, converts and then
* returns every {@link Throwable} and stacktrace found.
*
* @param throwable throwable to recursively convert
* @param indent if all lines shall be indented with a single tab.
* Enabling this will cause the output of this method
* to mimic the JVM's throwable handling output.
* @param includeHeader if to include the throwable header (see {@link #throwableHeader(Throwable)})
* @return full stack trace
* @since v1-alpha9
*/
public static @NotNull String stacktraceAsStringRecursive(@NotNull Throwable throwable, boolean indent, boolean includeHeader) {
StringBuilder output = new StringBuilder();
// Append header and stacktrace
if (includeHeader)
output
.append(throwableHeader(throwable))
.append("\n");
output.append(stacktraceAsString(throwable, indent));
// Process recursively
if (throwable.getCause() != null)
output
.append("\n")
.append(stacktraceAsStringRecursive(throwable.getCause(), indent, includeHeader));
if (throwable instanceof ClassNotFoundException exception)
output
.append("\n")
.append(stacktraceAsStringRecursive(exception.getException(), indent, includeHeader));
if (throwable instanceof ExceptionInInitializerError exception)
output
.append("\n")
.append(stacktraceAsStringRecursive(exception.getException(), indent, includeHeader));
if (throwable instanceof InvocationTargetException exception)
output
.append("\n")
.append(stacktraceAsStringRecursive(exception.getTargetException(), indent, includeHeader));
return output.toString();
}
}

View file

@ -1,85 +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.misc;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Manipulates strings and characters.
*
* @since v1-alpha9
*/
public final class StringUtil {
/**
* Counts the number of occurrences of the specified substring inside the specified string.
*
* @param string string to search in
* @param substring substring to count occurrences for
* @return number of times the specified substring was found inside the specified string
* @since v1-alpha9
*/
public static long count(@NotNull String string, @NotNull String substring) {
return (string.length() - string.replace(substring, "").length()) / substring.length();
}
/**
* Searches for the specified separators
* in the specified string and returns
* the first separator which matches the
* specified amount of required occurrences.
*
* @param string string to operate on
* @param separators separators to check for
* @param requiredOccurrences exact amount of occurrences for a separator to be deemed valid
* @return separator to use or {@code null}
* @since v1-alpha9
*/
public static @Nullable String getSeparatorRequired(@NotNull String string, @NotNull String @NotNull [] separators, int requiredOccurrences) {
if (string.isBlank() || separators.length == 0 || requiredOccurrences == 0)
return null;
for (String separator : separators)
if (count(string, separator) == requiredOccurrences)
return separator;
return null;
}
/**
* Searches for the specified separators
* in the specified string and returns
* the first separator which matches the
* specified amount of minimum occurrences.
*
* @param string string to operate on
* @param separators separators to check for
* @param minimumOccurrences minimum amount of occurrences for a separator to be deemed valid
* @return separator to use or {@code null}
* @since v1-alpha9
*/
public static @Nullable String getSeparatorMinimum(@NotNull String string, @NotNull String @NotNull [] separators, int minimumOccurrences) {
for (String separator : separators)
if (count(string, separator) >= minimumOccurrences)
return separator;
return null;
}
}

View file

@ -20,7 +20,7 @@
/**
* Provides (utility) classes specifically made for one task.
*
* @see de.staropensource.engine.base.utility.misc.Miscellaneous
* @see de.staropensource.engine.base.utility.Miscellaneous
* @since v1-alpha0
*/
package de.staropensource.engine.base.utility;

View file

@ -29,11 +29,11 @@ module sosengine.base {
exports de.staropensource.engine.base.implementation.logging;
exports de.staropensource.engine.base.reflection;
exports de.staropensource.engine.base.type;
exports de.staropensource.engine.base.type.immutable;
exports de.staropensource.engine.base.type.logging;
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.utility.misc;
// Reflection access
opens de.staropensource.engine.base;
@ -52,9 +52,9 @@ module sosengine.base {
opens de.staropensource.engine.base.implementation.logging;
opens de.staropensource.engine.base.reflection;
opens de.staropensource.engine.base.type;
opens de.staropensource.engine.base.type.immutable;
opens de.staropensource.engine.base.type.logging;
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.utility.misc;
}

View file

@ -21,9 +21,11 @@ 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.misc.NumberUtil;
import de.staropensource.engine.base.utility.misc.Miscellaneous;
import de.staropensource.engine.base.utility.Math;
import de.staropensource.engine.base.utility.Miscellaneous;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@ -64,7 +66,7 @@ public class MiscellaneousTest extends TestBase {
if (performMethodCalls("testPadNumbers", number, length, expected))
return;
String result = NumberUtil.padNumbers(number, length);
String result = Math.padNumbers(number, length);
assertEquals(expected, result, "Result \"" + result + "\" does not match expected output \"" + expected + "\"");
}
@ -96,7 +98,7 @@ public class MiscellaneousTest extends TestBase {
*/
@Test
@DisplayName("getMapValues")
void testSearchForValueInMap() {
void testGetMapValues() {
if (performMethodCalls("testGetMapValues"))
return;
@ -107,13 +109,28 @@ public class MiscellaneousTest extends TestBase {
testMap.put("keylast", "this is the last value");
testMap.put("keylast2", "this is the last value");
Set<?> output = Miscellaneous.searchForValueInMap(testMap, "this value exists twice");
Set<?> output = Miscellaneous.getMapValues(testMap, "this value exists twice");
assertTrue(output.contains("keytwo"), "Map key \"keytwo\" not found in output Set");
assertTrue(output.contains("keydrei"), "Map key \"keydrei\" not found in output Set");
assertEquals(2, output.size(), "There are more or less than two keys in the output Set");
}
/**
* Tests the method {@code executeSafely}.
*/
@Test
@DisplayName("executeSafely (test 0)")
void testExecuteSafely0() {
if (performMethodCalls("testExecuteSafely0"))
return;
throwableCaught = false;
Miscellaneous.executeSafely(() -> Logger.info("You can safely ignore this message (this comes from MiscellaneousTest#testExecuteSafely0)"), "MiscellaneousTest#testExecuteSafely0");
assertFalse(throwableCaught, "Event was triggered");
}
/**
* Tests the method {@code executeSafely}.
*/

View file

@ -30,7 +30,8 @@ tasks.register("javadocAll", Javadoc) {
":base",
":ansi",
":slf4j-compat",
":rendering",
":windowing",
":windowing:glfw",
":notification",
]

View file

@ -1 +1 @@
icon1-transparent.png
icon0.png

View file

@ -1 +1 @@
icon1.xcf
icon0.xcf

BIN
dist/branding/icon1-rawcog.xcf vendored Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 319 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 369 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

View file

@ -78,7 +78,7 @@ This avoids unnecessary bloat, having too many dependencies in your project whil
size and memory footprint of your project.
## Official subsystems
Besides the `base` engine, there are two stable subsystem and two experimental subsystems.
Besides the `base` engine, there are two stable subsystem and three 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.
@ -86,7 +86,8 @@ by the StarOpenSource Project directly and are not automatically updated with th
- [`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
### Experimental
- [`rendering`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/rendering): Provides an API for creating, managing and rendering windows
- [`windowing`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/windowing): Provides abstract APIs for creating and managing windows as well as monitors
- [`windowing-glfw`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/windowing/glfw): Windowing API; allows using [GLFW](https://glfw.org) for creating windows
- [`notification`](https://git.staropensource.de/StarOpenSource/Engine/src/branch/develop/notification): Provides an API for sending and receiving notifications inside a program
## API documentation

View file

@ -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.6.0",
"@docusaurus/plugin-client-redirects": "^3.6.0",
"@docusaurus/plugin-content-docs": "^3.6.0",
"@docusaurus/plugin-sitemap": "^3.6.0",
"@docusaurus/preset-classic": "3.6.0",
"@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.6.0",
"@docusaurus/tsconfig": "3.6.0",
"@docusaurus/types": "3.6.0",
"@types/node": "^20.12.5",
"typescript": "~5.4.4"
},

View file

@ -21,7 +21,7 @@
versioningCodename=Sugarcookie
versioningVersion=1
versioningType=alpha
versioningTyperelease=9
versioningTyperelease=8
versioningFork=
# Java

View file

@ -19,12 +19,12 @@
package de.staropensource.engine.notification;
import de.staropensource.engine.base.type.immutable.ImmutableHashMap;
import de.staropensource.engine.notification.type.Notification;
import de.staropensource.engine.notification.type.NotificationState;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -49,14 +49,14 @@ public final class NotificationManager {
private NotificationManager() {}
/**
* Returns an immutable map containing all
* Returns an {@link ImmutableHashMap} containing all
* registered notifications and their current states.
*
* @return map of registered notifications
* @since v1-alpha6
*/
public static @NotNull Map<@NotNull Notification, @NotNull NotificationState> getNotifications() {
return Collections.unmodifiableMap(notifications);
return new ImmutableHashMap<>(notifications);
}
/**

View file

@ -1,240 +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.Event;
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.utility.misc.NumberUtil;
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.misc.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.renderer.Renderer;
import de.staropensource.engine.rendering.type.Window;
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;
import org.lwjgl.glfw.*;
import java.util.ArrayList;
import java.util.Collections;
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();
// Initialize GLFW
initGlfw();
// Initialize renderer
Renderer.initialize();
// 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, RenderingPlatform.WAYLAND);
case X11 -> tryPlatform(GLFW_PLATFORM_X11, RenderingPlatform.X11);
case WIN32 -> tryPlatform(GLFW_PLATFORM_WIN32, RenderingPlatform.WIN32);
case COCOA -> tryPlatform(GLFW_PLATFORM_COCOA, RenderingPlatform.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 : new ArrayList<>(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 = Event.Priority.EXCLUSIVELY_IMPORTANT)
private static void logRenderingError(@NotNull String error) {
Logger.error("Rendering error occurred: " + error);
}
// -----> Utility methods
/**
* Checks if the specified platform is compatible,
* and if so, specifies it as the platform to use.
*
* @param platform platform to try
* @param renderingPlatform {@link RenderingPlatform} used to log that the platform is unsupported (set to {@code null} to disable)
* @since v1-alpha9
*/
private void tryPlatform(int platform, @Nullable RenderingPlatform renderingPlatform) {
if (glfwPlatformSupported(platform))
if (platform != GLFW_PLATFORM_WAYLAND)
glfwInitHint(GLFW_PLATFORM, platform);
else {
Logger.warn("Wayland is not supported by the StarOpenSource Engine due to various issues with it, sorry.");
tryPlatform(GLFW_PLATFORM_X11, RenderingPlatform.X11);
}
else {
if (renderingPlatform != null)
Logger.warn("Platform RenderingPlatform." + renderingPlatform.name() + " is not supported GLFW. Using RenderingPlatform.ANY instead");
glfwInitHint(GLFW_PLATFORM, GLFW_ANY_PLATFORM);
}
}
}

View file

@ -1,365 +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.RenderingAdapter;
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.
*
* @since v1-alpha9
* -- GETTER --
* Returns whether or not the delta time and
* FPS count should be logged to the console
* every second.
*
* @return print delta time and FPS count?
* @since v1-alpha9
*/
private boolean debugFrames;
/**
* Contains if to log the state of all
* windows every second.
*
* @since v1-alpha9
* -- GETTER --
* Returns if to log the state of all
* windows every second.
*
* @return log states of all windows?
* @since v1-alpha9
*/
private boolean debugWindowStates;
/**
* Contains if to allow updates to a window's
* position. May cause errors and crashes to
* appear, we do not know why.
*
* @since v1-alpha9
* -- GETTER --
* Returns if to allow updates to a window's
* position. May cause errors and crashes to
* appear, we do not know why.
*
* @return allow window position updates?
* @since v1-alpha9
*/
private boolean debugAllowPositionUpdates;
/**
* 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 the adapter bgfx shall use.
*
* @since v1-alpha9
* -- GETTER --
* Returns the adapter bgfx shall use.
*
* @return bgfx adapter
* @since v1-alpha9
*/
private RenderingAdapter renderingAdapter;
/**
* 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 "debugWindowStates" -> debugWindowStates = parser.getBoolean(group + property);
case "debugAllowPositionUpdates" -> debugAllowPositionUpdates = parser.getBoolean(group + property);
case "initialPlatform" -> {
try {
initialPlatform = RenderingPlatform.valueOf(parser.getString(group + property).toUpperCase());
} catch (IllegalArgumentException ignored) {
Logger.error("Rendering platform " + parser.getString(group + property) + " is not valid");
}
}
case "initialDisableLibdecor" -> initialDisableLibdecor = parser.getBoolean(group + property);
case "errorRenderingFailures" -> errorRenderingFailures = parser.getBoolean(group + property);
case "renderingAdapter" -> {
try {
renderingAdapter = RenderingAdapter.valueOf(parser.getString(group + property).toUpperCase());
} catch (IllegalArgumentException exception) {
Logger.error("Rendering adapter " + parser.getString(group + property) + " is not valid");
}
}
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;
debugWindowStates = false;
debugAllowPositionUpdates = false;
}
}
/** {@inheritDoc} */
@Override
public void loadDefaultConfiguration() {
debug = true;
debugInput = false;
debugFrames = true;
debugWindowStates = true;
debugAllowPositionUpdates = false;
initialPlatform = RenderingPlatform.ANY;
initialDisableLibdecor = false;
errorRenderingFailures = true;
renderingAdapter = RenderingAdapter.ANY;
vsyncMode = VsyncMode.OFF;
maximumFramesPerSecond = 0;
}
/** {@inheritDoc} */
@Override
public @Nullable Object getSetting(@NotNull String setting) {
switch (setting) {
case "debug" -> { return debug; }
case "debugInput" -> { return debugInput; }
case "debugFrames" -> { return debugFrames; }
case "debugWindowStates" -> { return debugWindowStates; }
case "debugAllowPositionUpdates" -> { return debugAllowPositionUpdates; }
case "initialPlatform" -> { return initialPlatform; }
case "disableLibdecor" -> { return initialDisableLibdecor; }
case "errorRenderingFailures" -> { return errorRenderingFailures; }
case "renderingAdapter" -> { return renderingAdapter; }
case "vsyncMode" -> { return vsyncMode; }
case "maximumFramesPerSecond" -> { return maximumFramesPerSecond; }
default -> { return null; }
}
}
}

View file

@ -1,384 +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.renderer;
import de.staropensource.engine.base.logging.Logger;
import de.staropensource.engine.base.utility.misc.Miscellaneous;
import de.staropensource.engine.base.utility.misc.NumberUtil;
import de.staropensource.engine.rendering.RenderingSubsystemConfiguration;
import de.staropensource.engine.rendering.exception.NotOnMainThreadException;
import de.staropensource.engine.rendering.type.FrameHandler;
import de.staropensource.engine.rendering.type.Window;
import de.staropensource.engine.rendering.type.window.VsyncMode;
import lombok.Getter;
import org.jetbrains.annotations.NotNull;
import java.time.LocalTime;
import java.util.*;
import static org.lwjgl.bgfx.BGFX.*;
import static org.lwjgl.glfw.GLFW.*;
/**
* Renders all windows out.
*
* @since v1-alpha9
*/
@SuppressWarnings({ "JavadocDeclaration" })
public final class Renderer {
/**
* Contains all frame handlers.
* <p>
* Frame handlers are invoked before
* all windows are rendered, allowing
* the application or game to respond
* to frame renders.
*
* @since v1-alpha9
*/
private static final @NotNull List<@NotNull FrameHandler> frameHandlers = Collections.synchronizedList(new ArrayList<>());
/**
* Contains if the renderer has been initialized.
*
* @since v1-alpha9
* -- GETTER --
* Returns if the renderer has been initialized.
*
* @return renderer initialized?
* @since v1-alpha9
*/
@Getter
private static boolean initialized = false;
/**
* Contains if the renderer is running.
*
* @since v1-alpha9
* -- GETTER --
* Returns if the renderer is running.
*
* @return renderer running?
* @since v1-alpha9
*/
@Getter
private static boolean running = false;
/**
* Contains the frame count aka.
* the amount of frames rendered.
* <p>
* Updated every frame.
*
* @since v1-alpha9
* -- GETTER --
* Returns the frame count aka.
* the amount of frames rendered.
* <p>
* Updated every frame.
*
* @return amount of frames rendered
* @since v1-alpha9
*/
@Getter
private static long frameCount = 0L;
/**
* Contains the delta time, also
* known as the render time.
* <p>
* Updated every second.
*
* @since v1-alpha9
* -- GETTER --
* Returns the delta time, also
* known as the render time.
* <p>
* Updated every second.
*
* @return delta time
* @since v1-alpha9
*/
@Getter
private static double deltaTime = 0d;
/**
* Contains the frames per second (FPS) count.
* <p>
* Updated every second.
*
* @since v1-alpha9
* -- GETTER --
* Returns the frames per second (FPS) count.
* <p>
* Updated every second.
*
* @return frames per second
* @since v1-alpha9
*/
@Getter
private static double framesPerSecond = 0d;
/**
* Contains the time it took
* to calculate the last frame.
* <p>
* Updated every frame.
*
* @since v1-alpha9
* -- GETTER --
* Contains the time it took
* to calculate the last frame.
* <p>
* Updated every frame.
*
* @return last frame time
* @since v1-alpha9
*/
@Getter
private static Map<@NotNull String, @NotNull Long> lastFrameTime = Collections.unmodifiableMap(new HashMap<>());
// ----> Initialization
/**
* Initializes the renderer.
*
* @since v1-alpha9
*/
public static void initialize() {
if (initialized)
return;
addFrameHandler(new FrameHandler() {
@Override
public @NotNull FrameHandler.Priority getPriority() {
return Priority.VERY_IMPORTANT;
}
@Override
public void run() {
bgfx_set_view_clear(0, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x00000000, 1.0f, 0);
}
});
addFrameHandler(new FrameHandler() {
/** {@inheritDoc} */
@Override
public @NotNull FrameHandler.Priority getPriority() {
return Priority.VERY_UNIMPORTANT;
}
/** {@inheritDoc} */
@Override
public void run() {
int offset = 2;
bgfx_dbg_text_clear(0, false);
bgfx_dbg_text_printf(0, 0, 0x0f, "It's " + NumberUtil.padNumbers(LocalTime.now().getHour(), 2) + ":" + NumberUtil.padNumbers(LocalTime.now().getMinute(), 2) + ":" + NumberUtil.padNumbers(LocalTime.now().getSecond(), 2));
bgfx_dbg_text_printf(0, 1, 0x0f, "LFT.size(): " + lastFrameTime.size());
for (String item : lastFrameTime.keySet()) {
bgfx_dbg_text_printf(0, offset, 0x0f, item + ": " + lastFrameTime.get(item) + "ms");
offset += 1;
}
}
});
initialized = true;
}
// -----> Frame handler management
/**
* Adds the specified frame handler.
*
* @param frameHandler {@link FrameHandler} to add
* @since v1-alpha9
*/
public static void addFrameHandler(@NotNull FrameHandler frameHandler) {
frameHandlers.add(frameHandler);
frameHandlers.sort(Comparator.comparing(FrameHandler::getPriority));
}
/**
* Removes the specified frame handler.
*
* @param frameHandler {@link FrameHandler} to remove
* @since v1-alpha9
*/
public static void removeFrameHandler(@NotNull FrameHandler frameHandler) {
frameHandlers.remove(frameHandler);
}
// -----> Rendering logic
/**
* Starts the renderer.
*
* @throws NotOnMainThreadException if not running on the main thread
* @throws RuntimeException on major rendering error
* @since v1-alpha9
*/
public static void start() throws RuntimeException {
// Check if running on main thread
if (!Miscellaneous.onMainThread())
throw new NotOnMainThreadException();
String threadName = Thread.currentThread().getName();
int threadPriority = Thread.currentThread().getPriority();
RuntimeException exception = null;
// Update thread
Thread.currentThread().setName("Rendering thread");
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
// Start renderer
running = true;
try {
render();
} catch (Throwable throwable) {
exception = new RuntimeException("Renderer failed", throwable);
}
// Revert thread changes
Thread.currentThread().setName(threadName);
Thread.currentThread().setPriority(threadPriority);
// Throw if necessary
if (exception != null)
throw exception;
}
/**
* Renders all windows.
*
* @since v1-alpha9
*/
@SuppressWarnings({ "InfiniteLoopStatement" })
private static void render() throws Throwable {
long previousFrameCount = 0L; // Frame count one second ago
LinkedList<Long> deltaTimeFractions = new LinkedList<>(); // Contains all delta time fractions
Map<String, Long> execTimes = new LinkedHashMap<>(); // Contains the amount of time of all rendering operations
long timesWait; // Time to wait until the next frame
long timesPSO = System.currentTimeMillis() + 1000; // Time to wait until invoking per-second operations
while (true) {
// Invoke frame handlers
for (FrameHandler frameHandler : frameHandlers)
execTimes.put("Frame handler '" + frameHandler.getClass().getName() + "'", Miscellaneous.measureExecutionTime(frameHandler::run));
// Perform rendering
execTimes.put("Rendering", Miscellaneous.measureExecutionTime(() -> {
// Poll for events
glfwPollEvents();
// Reset backbuffer
int resetSettings = 0;
if (RenderingSubsystemConfiguration.getInstance().getVsyncMode() == VsyncMode.ON)
resetSettings |= BGFX_RESET_TRANSPARENT_BACKBUFFER;
for (Window window : Window.getWindows())
if (window.isTransparent())
resetSettings |= BGFX_RESET_TRANSPARENT_BACKBUFFER;
bgfx_reset(
Window.getWindows().getFirst().getSize().getX(),
Window.getWindows().getFirst().getSize().getY(),
resetSettings,
BGFX_TEXTURE_FORMAT_RGBA4
);
// Render all windows
for (Window window : Window.getWindows())
if (window.isRendering()) {
window.updateState();
window.render();
}
// Advance to next frame
bgfx_frame(false);
}));
// Determine time to wait for the next frame
execTimes.put("Waiting", 0L);
if (RenderingSubsystemConfiguration.getInstance().getVsyncMode() == VsyncMode.OFF && RenderingSubsystemConfiguration.getInstance().getMaximumFramesPerSecond() > 0) {
execTimes.replace("Waiting", (long) (1d / RenderingSubsystemConfiguration.getInstance().getMaximumFramesPerSecond() * 1000d));
for (String time : execTimes.keySet())
if (!time.equals("Waiting"))
execTimes.replace("Waiting", execTimes.get("Waiting") - execTimes.get(time));
// Wait until next frame
if (execTimes.get("Waiting") > 0) {
timesWait = execTimes.get("Waiting") + System.currentTimeMillis();
while (System.currentTimeMillis() < timesWait)
Thread.onSpinWait();
}
}
// Perform per-frame operations
frameCount += 1;
lastFrameTime = new HashMap<>(execTimes);
deltaTimeFractions.add(execTimes.get("Rendering") + execTimes.get("Waiting"));
execTimes.clear();
// Perform per-second operations
if (System.currentTimeMillis() >= timesPSO) {
// Calculate delta time and FPS count
deltaTime = NumberUtil.calculateMeanLong(deltaTimeFractions);
framesPerSecond = 1000 / deltaTime;
// Log frame count
if (RenderingSubsystemConfiguration.getInstance().isDebugFrames())
Logger.diag("Frames " + previousFrameCount + "-" + frameCount + "\n-> Frames/s: " + framesPerSecond + "\n-> Delta time: " + deltaTime);
// Log window states
if (RenderingSubsystemConfiguration.getInstance().isDebugWindowStates())
for (Window window : Window.getWindows())
Logger.diag(
"Window state for " + window.getUniqueIdentifier() + "\n" +
"-> Terminated: " + window.isTerminated() + "\n" +
"-> Name: " + window.getName() + "\n" +
"-> Title: " + window.getTitle() + "\n" +
"-> Size: " + window.getSize() + "\n" +
" -> Minimum: " + window.getMinimumSize() + "\n" +
" -> Maximum: " + window.getMaximumSize() + "\n" +
"-> Position: " + window.getPosition() + "\n" +
"-> Mode: " + window.getMode() + "\n" +
"-> Resizable: " + window.isResizable() + "\n" +
"-> Borderless: " + window.isBorderless() + "\n" +
"-> Focused: " + window.isFocused() + "\n" +
"-> On top: " + window.isOnTop() + "\n" +
"-> Transparent: " + window.isTransparent() + "\n" +
"-> Rendering: " + window.isRendering()
);
// Reset per-second variables
previousFrameCount = frameCount;
deltaTimeFractions.clear();
timesPSO = System.currentTimeMillis() + 1000;
}
}
}
}

View file

@ -1,93 +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.type;
import org.jetbrains.annotations.NotNull;
/**
* Used for performing actions
* before a frame is rendered.
*
* @since v1-alpha9
*/
public interface FrameHandler {
/**
* Returns the priority this frame handler has.
*
* @return priority
* @since v1-alpha9
*/
@NotNull FrameHandler.Priority getPriority();
/**
* Invokes this frame handler.
*
* @since v1-alpha9
*/
void run();
/**
* Contains all priority levels
* a {@link FrameHandler} can have.
*
* @since v1-alpha9
*/
enum Priority {
/**
* {@link FrameHandler}s with
* this priority are called first.
*
* @since v1-alpha9
*/
VERY_IMPORTANT,
/**
* {@link FrameHandler}s with
* this priority are called 2nd.
*
* @since v1-alpha9
*/
IMPORTANT,
/**
* {@link FrameHandler}s with
* this priority are called 3rd.
*
* @since v1-alpha9
*/
DEFAULT,
/**
* {@link FrameHandler}s with
* this priority are called 4th.
*
* @since v1-alpha9
*/
UNIMPORTANT,
/**
* {@link FrameHandler}s with
* this priority are called last.
*
* @since v1-alpha9
*/
VERY_UNIMPORTANT,
}
}

View file

@ -1,76 +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.type.window;
/**
* Represents all available rendering adapters.
*
* @since v1-alpha9
*/
public enum RenderingAdapter {
/**
* Allows the bgfx to autodetect the adapter to use.
*
* @since v1-alpha9
*/
ANY,
/**
* Tells bgfx to use the CPU for rendering.
*
* @since v1-alpha9
*/
SOFTWARE,
/**
* Tells bgfx to use AMD graphics cards for rendering.
*
* @since v1-alpha9
*/
AMD,
/**
* Tells bgfx to use Apple's SOC for rendering.
*
* @since v1-alpha9
*/
APPLE,
/**
* Tells bgfx to use Intel graphics cards for rendering.
*
* @since v1-alpha9
*/
INTEL,
/**
* Tells bgfx to use NVIDIA graphics cards for rendering.
*
* @since v1-alpha9
*/
NVIDIA,
/**
* Tells bgfx to use Windows' integrated graphics driver for rendering.
*
* @since v1-alpha9
*/
MICROSOFT,
}

View file

@ -1,36 +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;
requires java.desktop;
// API access
exports de.staropensource.engine.rendering;
exports de.staropensource.engine.rendering.event;
exports de.staropensource.engine.rendering.exception;
exports de.staropensource.engine.rendering.renderer;
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.renderer;
opens de.staropensource.engine.rendering.type;
opens de.staropensource.engine.rendering.type.input;
opens de.staropensource.engine.rendering.type.window;
}

View file

@ -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")

View file

@ -23,6 +23,7 @@ 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.logging.LogLevel;
import de.staropensource.engine.base.utility.Miscellaneous;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Marker;
import org.slf4j.event.Level;

View file

@ -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
@ -86,9 +87,6 @@ application {
// reflective sclasspath scanning is disabled.
"-Dsosengine.base.initialIncludeSubsystemClasses=de.staropensource.engine.ansi.AnsiSubsystem,de.staropensource.engine.slf4j_compat.Slf4jCompatSubsystem,de.staropensource.engine.windowing.glfw.GlfwSubsystem",
// Force rendering platform
"-Dsosengine.rendering.initialPlatform=" + (project.hasProperty("renderingPlatform") ? project.property("renderingPlatform") as String : "any"),
// Force Jansi to write escape sequences
"-Djansi.mode=force",
]
@ -132,7 +130,6 @@ tasks.register('runNativeImage', Exec) {
"-Dsosengine.base.logForceStandardOutput=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",
"-Dsosengine.rendering.initialPlatform=" + (project.hasProperty("renderingPlatform") ? project.property("renderingPlatform") as String : "any"),
"-Djansi.mode=force",
)
executable("build/bin/sosengine-testapp")

View file

@ -25,16 +25,18 @@ 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.type.vector.Vec2i;
import de.staropensource.engine.rendering.event.InputEvent;
import de.staropensource.engine.rendering.renderer.Renderer;
import de.staropensource.engine.rendering.type.FrameHandler;
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.base.utility.Miscellaneous;
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;
import java.util.LinkedHashMap;
/**
* The main class of the sos!engine development application.
*
@ -117,6 +119,10 @@ public final class Main {
// Say hello to the 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;
try {
@ -133,28 +139,31 @@ public final class Main {
return;
}
if (window == null) {
Logger.crash("'window' is null");
return;
}
// Render loop
LinkedHashMap<@NotNull Window, @NotNull Throwable> renderLoopFailures = WindowingSubsystem
.getInstance()
.getApi()
.getManagement()
.runRenderLoopContinuously(() -> {
if (shutdown || window.isClosureRequested())
Engine.getInstance().shutdown();
});
// Add frame handlers
Renderer.addFrameHandler(new FrameHandler() {
@Override
public @NotNull FrameHandler.Priority getPriority() {
return Priority.DEFAULT;
}
@Override
public void run() {
if (shutdown || window.isClosureRequested())
Engine.getInstance().shutdown();
// Print render loop failures
StringBuilder message = new StringBuilder();
message.append("Render loop failed on some windows:\n");
//window.setPosition(new Vec2i((int) Renderer.getFrameCount() / 10, (int) Renderer.getFrameCount() / 10));
}
});
for (Window windowFailed : renderLoopFailures.keySet())
message
.append("-> ")
.append(window)
.append(": ")
.append(Miscellaneous.getStackTraceHeader(renderLoopFailures.get(windowFailed)))
.append("\n")
.append(Miscellaneous.getStackTraceAsString(renderLoopFailures.get(windowFailed), true))
.append("\n");
// Start renderer
Renderer.start();
Logger.crash(message.toString());
} catch (Exception exception) {
Logger.crash("Caught throwable in main thread:", exception);
}

View file

@ -7,5 +7,5 @@
open module sosengine.testapp {
// Dependencies
// -> Engine
requires sosengine.rendering;
requires sosengine.windowing;
}

109
windowing/build.gradle Normal file
View 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
View 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.

View file

@ -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
View file

@ -0,0 +1 @@
../../gradle

1
windowing/glfw/gradlew vendored Symbolic link
View file

@ -0,0 +1 @@
../../gradlew

1
windowing/glfw/gradlew.bat vendored Symbolic link
View file

@ -0,0 +1 @@
../../gradlew.bat

View file

@ -0,0 +1,218 @@
/*
* 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.logging.Logger;
import de.staropensource.engine.base.utility.information.EngineInformation;
import de.staropensource.engine.base.implementation.versioning.StarOpenSourceVersioningSystem;
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.implementation.GlfwInternalClass;
import de.staropensource.engine.windowing.glfw.implementation.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 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
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);
}
}

View file

@ -0,0 +1,139 @@
/*
* 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.logging.Logger;
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() {
instance = this;
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) {
Logger.error("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;
};
}
}

View file

@ -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.GLFWKeyCallbackI;
@ -31,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 {
/**
@ -39,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();
@ -47,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);

View file

@ -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

View file

@ -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;

View file

@ -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,13 +36,13 @@ 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;
@ -50,7 +50,7 @@ public class WindowCallback {
* Creates and initializes an instance of this abstract class.
*
* @param window {@link Window} class
* @since v1-alpha9
* @since v1-alpha2
*/
public WindowCallback(@NotNull Window window) {
this.attachedWindow = window;

View file

@ -0,0 +1,25 @@
/*
* 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/>.
*/
/**
* Interfaces and abstract classes which can be used for implementing classes.
*
* @since v1-alpha6
*/
package de.staropensource.engine.windowing.glfw.implementable;

View file

@ -0,0 +1,82 @@
/*
* 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.implementation;
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
@Setter
@SuppressWarnings({ "JavadocDeclaration" })
public final class GlfwInternalClass implements ApiInternalClass {
/**
* 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
*/
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;
}
}

View file

@ -0,0 +1,92 @@
/*
* 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.implementation;
import de.staropensource.engine.base.logging.Logger;
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 {
/**
* 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);
}
}

View file

@ -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.implementation;
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();
}
}

View file

@ -0,0 +1,566 @@
/*
* 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.implementation;
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 lombok.Setter;
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 code to execute in {@link #render()}.
*
* @since v1-alpha8
* -- GETTER --
* Returns the code to execute in {@link #render()}.
*
* @return rendering code
* @since v1-alpha8
* -- SETTER --
* Sets the code to execute in {@link #render()}
*
* @param renderCode new rendering code
* @since v1-alpha8
*/
@Getter
@Setter
private @NotNull Runnable renderCode = () -> glfwSwapBuffers(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, @NotNull Path @Nullable [] 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()));
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_FALSE);
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)
Logger.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();
renderCode.run();
}
// ------------------------------------------------ [ 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(@NotNull Path @Nullable [] icons) {
// Ensure the window is not terminated
if (isTerminated())
return;
this.icons = icons;
if (icons != null)
try (GLFWImage.Buffer iconsBuffer = GLFWImage.malloc(icons.length)) {
Logger.warn("GlfwWindow#setIcons is experimental and may cause engine or JVM crashes. Here be dragons!");
Logger.diag("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) {
Logger.diag("iterating icons » " + iconBuffers.size() + " » " + filepath);
// Load icon
Logger.diag("loading icon");
iconBuffers.add(STBImage.stbi_load(filepath.toAbsolutePath().toString(), width, height, channels, 4));
if (iconBuffers.getLast() == null) {
Logger.warn("Icon " + iconsBuffer.position() + " could not be loaded" + (STBImage.stbi_failure_reason() == null ? "" : ": " + STBImage.stbi_failure_reason()));
continue;
}
// Save into 'iconsBuffer'
Logger.diag("saving into buffer");
iconsBuffer
.position(iconsBuffer.position() + 1)
.width(width.get(0))
.height(height.get(0))
.pixels(iconBuffers.getLast());
}
Logger.diag("out of iteration");
// Set icons
Logger.diag("setting position");
iconsBuffer.position(0);
Logger.diag("setting icons");
Logger.flush();
glfwSetWindowIcon(identifierLong, iconsBuffer);
// Free icons
Logger.diag("freeing icons");
for (ByteBuffer buffer : iconBuffers)
if (buffer != null) {
Logger.diag("freeing buffer");
STBImage.stbi_image_free(buffer);
} else
Logger.diag("skipping null buffer");
}
}
/** {@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();
}
}

View file

@ -0,0 +1,25 @@
/*
* 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/>.
*/
/**
* Implementations for various interfaces and abstract classes.
*
* @since v1-alpha6
*/
package de.staropensource.engine.windowing.glfw.implementation;

View file

@ -18,8 +18,8 @@
*/
/**
* Miscellaneous utility classes.
* Code of the GLFW subsystem.
*
* @since v1-alpha9
* @since v1-alpha2
*/
package de.staropensource.engine.base.utility.misc;
package de.staropensource.engine.windowing.glfw;

View file

@ -17,53 +17,53 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.staropensource.engine.rendering.type.window;
package de.staropensource.engine.windowing.glfw.type;
/**
* Represents all available rendering platforms.
* Contains all available platforms which GLFW can be initialized with.
*
* @since v1-alpha9
* @since v1-alpha2
*/
public enum RenderingPlatform {
public enum GlfwPlatform {
/**
* Allows GLFW to autodetect the platform to use.
*
* @since v1-alpha9
* @since v1-alpha2
*/
ANY,
/**
* Prefer initializing with the Wayland platform.
*
* @since v1-alpha9
* @since v1-alpha2
*/
WAYLAND,
/**
* Prefer initializing with the X11 platform.
*
* @since v1-alpha9
* @since v1-alpha2
*/
X11,
/**
* Prefer initializing with the Win32 platform.
*
* @since v1-alpha9
* @since v1-alpha2
*/
WIN32,
/**
* Prefer initializing with the Cocoa platform.
*
* @since v1-alpha9
* @since v1-alpha2
*/
COCOA,
/**
* Prefer initializing headless.
* Prefer initializing without any platform.
*
* @since v1-alpha9
* @since v1-alpha2
*/
NONE
}

View file

@ -0,0 +1,25 @@
/*
* 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/>.
*/
/**
* Data types in form of enums and classes.
*
* @since v1-alpha2
*/
package de.staropensource.engine.windowing.glfw.type;

View file

@ -0,0 +1,25 @@
/**
* The {@code glfw} subsystem and windowing API, which makes it
* possible to create {@link Window} using the GLFW library.
*
* @since v1-alpha4
*/
module sosengine.windowing.glfw {
// Dependencies
// -> Engine
requires transitive sosengine.base;
requires transitive sosengine.windowing;
// -> Libraries
requires transitive static lombok;
requires transitive org.jetbrains.annotations;
requires org.lwjgl.glfw;
requires org.lwjgl.stb;
// API access
exports de.staropensource.engine.windowing.glfw;
exports de.staropensource.engine.windowing.glfw.implementation;
// Reflection access
opens de.staropensource.engine.windowing.glfw;
opens de.staropensource.engine.windowing.glfw.implementation;
}

Some files were not shown because too many files have changed in this diff Show more