diff --git a/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/Miscellaneous.kt b/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/Miscellaneous.kt
new file mode 100644
index 000000000..9d0c817d3
--- /dev/null
+++ b/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/Miscellaneous.kt
@@ -0,0 +1,46 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Authors
+ * Licensed under the GNU Affero General Public License v3
+ * with an exception allowing classpath linking.
+ *
+ * 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 .
+ */
+
+package de.staropensource.engine.base.utility.misc
+
+/**
+ * REALLY miscellaneous methods are in here.
+ *
+ * @since v1-alpha10
+ */
+class Miscellaneous private constructor() {
+ /**
+ * Companion object of [Miscellaneous].
+ *
+ * @since v1-alpha10
+ */
+ companion object {
+ /**
+ * Returns if currently
+ * running on the main thread.
+ *
+ * @return `true` if running on the main thread, `false` if not, `null` if not supported (e.g. not running on the JVM)
+ * @since v1-alpha10
+ */
+ fun onMainThread(): Boolean? {
+ return Thread.currentThread().threadId() == 1L
+ }
+ }
+}
diff --git a/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/StackTraceUtils.kt b/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/StackTraceUtils.kt
new file mode 100644
index 000000000..2bfabdaa6
--- /dev/null
+++ b/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/StackTraceUtils.kt
@@ -0,0 +1,165 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Authors
+ * Licensed under the GNU Affero General Public License v3
+ * with an exception allowing classpath linking.
+ *
+ * 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 .
+ */
+
+package de.staropensource.engine.base.utility.misc
+
+import de.staropensource.engine.base.type.Origin
+import java.lang.reflect.InvocationTargetException
+
+/**
+ * Utility methods for stack trace
+ * and exception analysis.
+ *
+ * @since v1-alpha10
+ */
+class StackTraceUtils private constructor() {
+ /**
+ * Companion object of [StackTraceUtils].
+ *
+ * @since v1-alpha10
+ */
+ companion object {
+ /**
+ * Returns the method caller.
+ *
+ * @param depth how deep to go into the stack trace
+ * @return deepest method caller
+ * @since v1-alpha10
+ */
+ fun getMethodCaller(depth: UInt = 0u): Origin {
+ val stacktrace: Array = Throwable().stackTrace
+ var element: StackTraceElement? = null
+
+ // Get wanted element
+ element = if (depth.plus(2u).toInt() > stacktrace.size)
+ stacktrace[stacktrace.size - 1]
+ else
+ stacktrace[depth.plus(2u).toInt()]
+
+ // Return origin
+ return Origin(
+ packageName = element.className.substringBeforeLast('.'),
+ className = element.className.substringAfterLast('.'),
+ methodName = element.methodName,
+ lineNumber = element.lineNumber.toUInt(),
+ )
+ }
+
+
+ /**
+ * Returns the `Caused by: ` header
+ * usually found in JVM stacktraces.
+ *
+ * @param throwable [Throwable] to use
+ * @return stacktrace header
+ * @since v1-alpha10
+ */
+ fun stacktraceHeader(throwable: Throwable): String {
+ return "Caused by: ${throwable.javaClass.name}${if (throwable.message == null) "" else ": ${throwable.message}"}"
+ }
+
+ /**
+ * Returns the body of the stacktrace
+ * as it is usually found in JVM
+ * stacktraces.
+ *
+ * @param throwable [Throwable] to use
+ * @param indent if to add a tab character (`\t`) as indentation
+ * @return stacktrace body
+ * @since v1-alpha10
+ */
+ fun stacktraceBody(throwable: Throwable, indent: Boolean = true): String = buildString {
+ for (element: StackTraceElement in throwable.stackTrace) {
+ if (!isEmpty())
+ append("\n")
+ if (indent)
+ append("\t")
+
+ append("at ")
+ append(element)
+ }
+ }
+
+ /**
+ * Returns a recursively resolved
+ * collection of stacktraces, all
+ * derived from the passed [throwable].
+ *
+ * Use this method if you intend to
+ * print an exception's stacktrace or
+ * want to display it nicely elsewhere.
+ *
+ *
+ * @param throwable [Throwable] to use
+ * @param indent if to add a tab character (`\t`) as indentation
+ * @param includeHeader if to include the `Caused by` stacktrace header (see [stacktraceHeader])
+ * @return recursively resolved stacktrace
+ * @since v1-alpha10
+ */
+ fun stacktraceRecursive(throwable: Throwable, indent: Boolean = true, includeHeader: Boolean = true, depth: UInt = 10u): String = buildString {
+ // Append header
+ if (includeHeader) {
+ append(stacktraceHeader(throwable))
+ append("\n")
+ }
+
+ // Append body
+ append(stacktraceBody(throwable, indent = indent))
+
+ // Recurse downwards
+ if (throwable.cause != null) {
+ append("\n")
+ if (depth == 0u)
+ append("...")
+ else
+ append(stacktraceRecursive(throwable.cause!!, indent = indent, includeHeader = includeHeader, depth = depth.minus(1u)))
+ }
+
+ if (throwable is ClassNotFoundException) {
+ val exception: ClassNotFoundException = throwable
+ if (exception.exception != null) {
+ append("\n")
+ if (depth == 0u)
+ append("...")
+ else
+ append(stacktraceRecursive(exception.exception, indent = indent, includeHeader = includeHeader, depth = depth.minus(1u)))
+ }
+ } else if (throwable is ExceptionInInitializerError) {
+ val exception: ExceptionInInitializerError = throwable
+ if (exception.exception != null) {
+ append("\n")
+ if (depth == 0u)
+ append("...")
+ else
+ append(stacktraceRecursive(exception.exception, indent = indent, includeHeader = includeHeader, depth = depth.minus(1u)))
+ }
+ } else if (throwable is InvocationTargetException) {
+ val exception: InvocationTargetException = throwable
+ if (exception.targetException != null) {
+ append("\n")
+ if (depth == 0u)
+ append("...")
+ else
+ append(stacktraceRecursive(exception.targetException, indent = indent, includeHeader = includeHeader, depth = depth.minus(1u)))
+ }
+ }
+ }
+ }
+}
diff --git a/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/package-info.kt b/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/package-info.kt
new file mode 100644
index 000000000..0bc45d362
--- /dev/null
+++ b/base/src/main/kotlin/de/staropensource/engine/base/utility/misc/package-info.kt
@@ -0,0 +1,26 @@
+/*
+ * STAROPENSOURCE ENGINE SOURCE FILE
+ * Copyright (c) 2024 The StarOpenSource Engine Authors
+ * Licensed under the GNU Affero General Public License v3
+ * with an exception allowing classpath linking.
+ *
+ * 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 .
+ */
+
+/**
+ * Small, miscellaneous utility classes.
+ *
+ * @since v1-alpha10
+ */
+package de.staropensource.engine.base.utility.misc