Add events
All checks were successful
PRs & Pushes / test (push) Successful in 2m51s
PRs & Pushes / build-jars (push) Successful in 2m58s
PRs & Pushes / build-apidoc (push) Successful in 2m57s

This commit is contained in:
JeremyStar™ 2024-12-29 18:48:05 +01:00
parent 0f229d51d7
commit 2ae5c3665e
Signed by: JeremyStarTM
GPG key ID: E366BAEF67E4704D
13 changed files with 706 additions and 8 deletions

View file

@ -19,8 +19,10 @@
package de.staropensource.engine.base
import de.staropensource.engine.base.Engine.Companion.bootstrap
import de.staropensource.engine.base.exception.EngineInitializationFailureException
import de.staropensource.engine.base.implementable.Subsystem
import de.staropensource.engine.base.implementation.event.*
import de.staropensource.engine.base.logging.Logger
import de.staropensource.engine.base.utility.Environment
import de.staropensource.engine.base.utility.FileAccess
@ -58,7 +60,10 @@ class Engine private constructor() {
* @since v1-alpha10
*/
var state: State = State.UNINITIALIZED
internal set
internal set(value: State) {
field = value
EngineStateChangeEvent.instance.emit(value)
}
/**
* Contains if the engine is currently
@ -155,9 +160,10 @@ class Engine private constructor() {
/**
* Bootstraps the engine.
*
* This method initializes static variables
* and generally performs actions which
* should only be executed once.
* This method initializes static
* variables and generally performs
* actions which should only be
* performed once.
*
* @return `true` if bootstrapping was performed successfully, `false` if failed
* @throws Throwable on initialization error
@ -193,6 +199,9 @@ class Engine private constructor() {
)
}
// Emit EngineBootstrapEvent
EngineBootstrapEvent.instance.emit()
bootstrapping = false
return true
} catch (exception: Exception) {
@ -250,6 +259,9 @@ class Engine private constructor() {
)
}
// Emit EngineInitializationEvent
EngineInitializationEvent.instance.emit()
state = State.INITIALIZED
// Print initialization message
@ -307,6 +319,9 @@ class Engine private constructor() {
fatal = true
)
}
// Emit EngineReloadEvent
EngineReloadEvent.instance.emit()
}
/**
@ -376,11 +391,11 @@ class Engine private constructor() {
/**
* Shuts the engine down.
*
* This performs the actual shutdown,
* just without the bloat.
* This method performs
* the actual shutdown.
*
* @param final whether this is the last time the engine shuts down. Doesn't actually shut the application down, just changes some messages and does other things
* @param crashed enables super careful mode to prevent further breakage
* @param final whether this is the last time the engine shuts down. Note that enabling this flag does not kill the process by itself
* @param crashed whether to enable super careful mode to prevent further breakage
* @since v1-alpha10
*/
@JvmStatic
@ -411,6 +426,9 @@ class Engine private constructor() {
}
}
// Emit EngineShutdownEvent
EngineShutdownEvent.instance.emit(final, crashed)
// Print shutdown message
if (final)
logger.info("""

View file

@ -0,0 +1,136 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU General Public License v3.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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.implementable
import de.staropensource.engine.base.Engine.Companion.logger
import de.staropensource.engine.base.utility.misc.StackTraceUtils
/**
* An event.
*
* It's highly recommended that
* the implementing class provides
* a global instance of itself.
*
* @constructor Initializes this event
* @since v1-alpha10
*/
abstract class Event {
/**
* Contains all registered listeners.
*
* @since v1-alpha10
*/
private val listeners: MutableSet<Map<String, Any?>.() -> Unit> = mutableSetOf()
// -----> Listeners
/**
* Removes a listener from this [Event].
*
* @return registered listeners
* @since v1-alpha10
*/
fun getListeners(): Set<Map<String, Any?>.() -> Unit> = listeners.toSet()
/**
* Adds a listener to this [Event].
*
* @param action listener to add
* @return this instance
* @since v1-alpha10
*/
fun addListener(action: Map<String, Any?>.() -> Unit): Event {
listeners.add(action)
return this
}
/**
* Removes a listener from this [Event].
*
* @param action listener to remove
* @return this instance
* @since v1-alpha10
*/
fun removeListener(action: Map<String, Any?>.() -> Unit): Event {
listeners.remove(action)
return this
}
// -----> Invocation
/**
* Emits this event with the
* specified arguments.
*
* Invoking this method will
* block the flow of execution
* until all [Event] listeners
* have been invoked.
*
* @param arguments arguments to pass to all listeners
* @return this instance
* @since v1-alpha10
*/
@OptIn(ExperimentalStdlibApi::class)
protected fun internalEmit(arguments: Map<String, Any?>): Event {
// Log about event emission
logger.diag(buildString {
append("Event ${this@Event::class.qualifiedName ?: "<anonymous>"} was emitted ")
if (arguments.isEmpty())
append("without any arguments")
else {
append("with arguments:")
// Iterate over arguments
var value: Any?
for (argument: String in arguments.keys) {
value = arguments[argument]
append("\n${argument} = ")
// Pretty formatting
if (value is Enum<*>)
append("${value::class.qualifiedName ?: "<anonymous enum>"}.${value.name}")
else if (value is CharSequence)
append("\"${value}\"")
else if (value is Char)
append("'${value}'")
else if (value is Byte)
append("0x${value.toHexString(HexFormat.UpperCase)}")
else if (value is ByteArray)
append("0x[ ${value.toHexString(HexFormat.UpperCase)} ]")
else
append(value.toString())
}
}
})
// Invoke all listeners
listeners.forEach { listener -> {
try {
listener.invoke(arguments)
} catch (throwable: Throwable) {
logger.error("A listener on event ${this::class.qualifiedName ?: "<anonymous>"} threw ${throwable::class.qualifiedName ?: "<anonymous>"}\n\n${StackTraceUtils.stacktraceRecursive(throwable)}")
}
} }
return this
}
}

View file

@ -0,0 +1,53 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU General Public License v3.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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.implementable
/**
* An implementation of [Event]
* without any arguments.
*
* It's highly recommended that
* the implementing class provides
* a global instance of itself.
*
* *This is an exact replica of
* [NoArgumentsEvent], just with
* the side-effect of [emit] being
* an `internal` method. This is
* used for [Event]s which must only
* be emitted by the engine itself.*
*
* @constructor Initializes this event
* @since v1-alpha10
*/
abstract class InternalNoArgumentsEvent internal constructor() : Event() {
/**
* Emits this event.
*
* Invoking this method will
* block the flow of execution
* until all [Event] listeners
* have been invoked.
*
* @return this instance
* @since v1-alpha10
*/
internal fun emit(): Event = internalEmit(mapOf())
}

View file

@ -0,0 +1,46 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU General Public License v3.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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.implementable
/**
* An implementation of [Event]
* without any arguments.
*
* It's highly recommended that
* the implementing class provides
* a global instance of itself.
*
* @constructor Initializes this event
* @since v1-alpha10
*/
open class NoArgumentsEvent : Event() {
/**
* Calls this event.
*
* Invoking this method will
* block the flow of execution
* until all [Event] listeners
* have been invoked.
*
* @return this instance
* @since v1-alpha10
*/
fun emit(): Event = internalEmit(mapOf())
}

View file

@ -0,0 +1,47 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU General Public License v3.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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.implementation.event
import de.staropensource.engine.base.implementable.InternalNoArgumentsEvent
/**
* Emitted after the engine
* has bootstrapped itself.
*
* This event has no arguments.
*
* @constructor Initializes this event
* @since v1-alpha10
*/
class EngineBootstrapEvent private constructor() : InternalNoArgumentsEvent() {
/**
* Companion object of [EngineBootstrapEvent].
*
* @since v1-alpha10
*/
companion object {
/**
* Global instance of [EngineBootstrapEvent].
*
* @since v1-alpha10
*/
val instance: EngineBootstrapEvent = EngineBootstrapEvent()
}
}

View file

@ -0,0 +1,47 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU General Public License v3.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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.implementation.event
import de.staropensource.engine.base.implementable.InternalNoArgumentsEvent
/**
* Emitted after the engine
* has initialized itself.
*
* This event has no arguments.
*
* @constructor Initializes this event
* @since v1-alpha10
*/
class EngineInitializationEvent private constructor() : InternalNoArgumentsEvent() {
/**
* Companion object of [EngineInitializationEvent].
*
* @since v1-alpha10
*/
companion object {
/**
* Global instance of [EngineInitializationEvent].
*
* @since v1-alpha10
*/
val instance: EngineInitializationEvent = EngineInitializationEvent()
}
}

View file

@ -0,0 +1,47 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU General Public License v3.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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.implementation.event
import de.staropensource.engine.base.implementable.InternalNoArgumentsEvent
/**
* Emitted after the engine
* has reloaded itself.
*
* This event has no arguments.
*
* @constructor Initializes this event
* @since v1-alpha10
*/
class EngineReloadEvent private constructor() : InternalNoArgumentsEvent() {
/**
* Companion object of [EngineReloadEvent].
*
* @since v1-alpha10
*/
companion object {
/**
* Global instance of [EngineReloadEvent].
*
* @since v1-alpha10
*/
val instance: EngineReloadEvent = EngineReloadEvent()
}
}

View file

@ -0,0 +1,70 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU General Public License v3.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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.implementation.event
import de.staropensource.engine.base.implementable.Event
/**
* Emitted after the state
* of the engine has changed.
*
* This event has two arguments:
* ```markdown
* | NAME | TYPE | PURPOSE/DESCRIPTION |
* | final | Boolean | whether this is the last time the engine shuts down |
* | crashed | Boolean | whether to enable super careful mode to prevent further breakage |
* ```
*
* @constructor Initializes this event
* @since v1-alpha10
*/
class EngineShutdownEvent private constructor() : Event() {
/**
* Companion object of [EngineShutdownEvent].
*
* @since v1-alpha10
*/
companion object {
/**
* Global instance of [EngineShutdownEvent].
*
* @since v1-alpha10
*/
val instance: EngineShutdownEvent = EngineShutdownEvent()
}
/**
* Emits this event.
*
* Invoking this method will
* block the flow of execution
* until all [Event] listeners
* have been invoked.
*
* @param final whether this is the last time the engine shuts down
* @param crashed whether to enable super careful mode to prevent further breakage
* @return this instance
* @since v1-alpha10
*/
internal fun emit(final: Boolean, crashed: Boolean): Event = internalEmit(mapOf(
Pair("final", final),
Pair("crashed", crashed)
))
}

View file

@ -0,0 +1,68 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU General Public License v3.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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.implementation.event
import de.staropensource.engine.base.Engine
import de.staropensource.engine.base.implementable.Event
/**
* Emitted after the state
* of the engine has changed.
*
* This event has one argument:
* ```markdown
* | NAME | TYPE | PURPOSE/DESCRIPTION |
* | state | Engine.State | the new engine state |
* ```
*
* @constructor Initializes this event
* @since v1-alpha10
*/
class EngineStateChangeEvent private constructor() : Event() {
/**
* Companion object of [EngineStateChangeEvent].
*
* @since v1-alpha10
*/
companion object {
/**
* Global instance of [EngineStateChangeEvent].
*
* @since v1-alpha10
*/
val instance: EngineStateChangeEvent = EngineStateChangeEvent()
}
/**
* Emits this event.
*
* Invoking this method will
* block the flow of execution
* until all [Event] listeners
* have been invoked.
*
* @param state new engine [State]
* @return this instance
* @since v1-alpha10
*/
internal fun emit(state: Engine.State): Event = internalEmit(mapOf(
Pair("state", state)
))
}

View file

@ -0,0 +1,68 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU General Public License v3.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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.implementation.event
import de.staropensource.engine.base.implementable.Event
import de.staropensource.engine.base.utility.Process
/**
* Emitted after a [Process]
* has been spawned.
*
* This event has one argument:
* ```markdown
* | NAME | TYPE | PURPOSE/DESCRIPTION |
* | process | Process | newly spawned process |
* ```
*
* @constructor Initializes this event
* @since v1-alpha10
*/
class ProcessSpawnEvent private constructor() : Event() {
/**
* Companion object of [ProcessSpawnEvent].
*
* @since v1-alpha10
*/
companion object {
/**
* Global instance of [ProcessSpawnEvent].
*
* @since v1-alpha10
*/
val instance: ProcessSpawnEvent = ProcessSpawnEvent()
}
/**
* Emits this event.
*
* Invoking this method will
* block the flow of execution
* until all [Event] listeners
* have been invoked.
*
* @param process newly spawned [Process]
* @return this instance
* @since v1-alpha10
*/
internal fun emit(process: Process): Event = internalEmit(mapOf(
Pair("process", process)
))
}

View file

@ -0,0 +1,68 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU General Public License v3.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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.implementation.event
import de.staropensource.engine.base.implementable.Event
import de.staropensource.engine.base.implementable.Subsystem
/**
* Emitted after a subsystem
* has been registered.
*
* This event has one argument:
* ```markdown
* | NAME | TYPE | PURPOSE/DESCRIPTION |
* | subsystem | Subsystem | newly registered subsystem |
* ```
*
* @constructor Initializes this event
* @since v1-alpha10
*/
class SubsystemRegistrationEvent private constructor() : Event() {
/**
* Companion object of [SubsystemRegistrationEvent].
*
* @since v1-alpha10
*/
companion object {
/**
* Global instance of [SubsystemRegistrationEvent].
*
* @since v1-alpha10
*/
val instance: SubsystemRegistrationEvent = SubsystemRegistrationEvent()
}
/**
* Emits this event.
*
* Invoking this method will
* block the flow of execution
* until all [Event] listeners
* have been invoked.
*
* @param subsystem newly registered [Subsystem]
* @return this instance
* @since v1-alpha10
*/
internal fun emit(subsystem: Subsystem): Event = internalEmit(mapOf(
Pair("subsystem", subsystem)
))
}

View file

@ -0,0 +1,25 @@
/*
* STAROPENSOURCE ENGINE SOURCE FILE
* Copyright (c) 2024 The StarOpenSource Engine Authors
* Licensed under the GNU General Public License v3.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU 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/>.
*/
/**
* Events emitted by the core engine.
*
* @since v1-alpha10
*/
package de.staropensource.engine.base.implementation.event

View file

@ -25,11 +25,13 @@ import de.staropensource.engine.base.exception.io.IOAccessException
import de.staropensource.engine.base.implementable.stream.ReadStream
import de.staropensource.engine.base.implementable.stream.Stream
import de.staropensource.engine.base.implementable.stream.WriteStream
import de.staropensource.engine.base.implementation.event.ProcessSpawnEvent
import de.staropensource.engine.base.implementation.stream.JavaReadStream
import de.staropensource.engine.base.implementation.stream.JavaWriteStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import kotlin.Throws
/**
* Provides a simplified way of
@ -199,6 +201,9 @@ class Process {
standardOutput.watch()
standardError.watch()
}
// Emit ProcessSpawnEvent
ProcessSpawnEvent.instance.emit(this)
} catch (exception: IOException) {
throw IOAccessException("Unable to spawn new process", exception)
}