From d308f198451c48c85c92d0bf390fdb40bd22c5d4 Mon Sep 17 00:00:00 2001 From: Emanuel Rabina Date: Fri, 2 Feb 2024 22:48:36 +1300 Subject: [PATCH] Use local rate limiting to remove need for the rate limiting class --- .../redhorizon/engine/EngineSystem.groovy | 19 +++++++ .../engine/audio/AudioSystem.groovy | 51 ++++++++++--------- .../redhorizon/engine/time/GameClock.groovy | 35 ++++++------- 3 files changed, 64 insertions(+), 41 deletions(-) diff --git a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/EngineSystem.groovy b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/EngineSystem.groovy index 2193ba30..df9651c5 100644 --- a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/EngineSystem.groovy +++ b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/EngineSystem.groovy @@ -34,4 +34,23 @@ abstract class EngineSystem implements Runnable, EventTarget { this.scene = scene } + + /** + * Execute an action and optionally wait, such that, if repeated, it would run + * no faster than the given frequency. + * + * @param frequency + * The number of times per second the action could be repeated. + * @param action + * @return + */ + protected static void rateLimit(float frequency, Closure action) { + + var maxExecTime = 1000f / frequency + var execTime = time(action) + var waitTime = maxExecTime - execTime + if (waitTime > 0) { + Thread.sleep((long)waitTime) + } + } } diff --git a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/audio/AudioSystem.groovy b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/audio/AudioSystem.groovy index 8bf38fd6..374ed9fd 100644 --- a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/audio/AudioSystem.groovy +++ b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/audio/AudioSystem.groovy @@ -44,8 +44,10 @@ class AudioSystem extends EngineSystem { final AudioConfiguration config // For object lifecycles + // TODO: Move to using the 'request' system from the scripting branch to remove these private final CopyOnWriteArrayList addedElements = new CopyOnWriteArrayList<>() private final CopyOnWriteArrayList removedElements = new CopyOnWriteArrayList<>() + private final Set initialized = new HashSet<>() /** * Constructor, build a new engine for rendering audio. @@ -87,39 +89,40 @@ class AudioSystem extends EngineSystem { logger.debug('Audio system in render loop...') while (!Thread.interrupted()) { try { - - // Initialize or delete objects which have been added/removed to/from the scene - if (addedElements) { - def elementsToInit = new ArrayList(addedElements) - elementsToInit.each { elementToInit -> - elementToInit.accept { element -> - if (element instanceof AudioElement) { - element.init(renderer) + rateLimit(100) { -> + + // Initialize or delete objects which have been added/removed to/from the scene + if (addedElements) { + def elementsToInit = new ArrayList(addedElements) + elementsToInit.each { elementToInit -> + elementToInit.accept { element -> + if (element instanceof AudioElement) { + element.init(renderer) + initialized << element + } } } + addedElements.removeAll(elementsToInit) } - addedElements.removeAll(elementsToInit) - } - if (removedElements) { - def elementsToDelete = new ArrayList(removedElements) - elementsToDelete.each { elementToInit -> - elementToInit.accept { element -> - if (element instanceof AudioElement) { - element.delete(renderer) + if (removedElements) { + def elementsToDelete = new ArrayList(removedElements) + elementsToDelete.each { elementToInit -> + elementToInit.accept { element -> + if (element instanceof AudioElement) { + element.delete(renderer) + } } } + removedElements.removeAll(elementsToDelete) } - removedElements.removeAll(elementsToDelete) - } - // Run the audio elements - scene.accept { element -> - if (element instanceof AudioElement) { - element.render(renderer) + // Run the audio elements + scene.accept { element -> + if (element instanceof AudioElement && initialized.contains(element)) { + element.render(renderer) + } } } - - Thread.sleep(10) } catch (InterruptedException ignored) { break diff --git a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/time/GameClock.groovy b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/time/GameClock.groovy index cded176e..a25b2c25 100644 --- a/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/time/GameClock.groovy +++ b/redhorizon-engine/source/nz/net/ultraq/redhorizon/engine/time/GameClock.groovy @@ -85,27 +85,28 @@ class GameClock extends EngineSystem { logger.debug('Game clock in update loop') while (!Thread.interrupted()) { try { - Thread.sleep(10) - var currentSystemTimeMillis = System.currentTimeMillis() - var delta = currentSystemTimeMillis - lastSystemTimeMillis + rateLimit(100) { -> + var currentSystemTimeMillis = System.currentTimeMillis() + var delta = currentSystemTimeMillis - lastSystemTimeMillis - // Normal flow of time, accumulate ticks at the same rate as system time - if (speed == 1.0f) { - currentTimeMillis += delta - } - // Modified flow, accumulate ticks at system time * flow speed - else { - currentTimeMillis += (delta * speed) - } + // Normal flow of time, accumulate ticks at the same rate as system time + if (speed == 1.0f) { + currentTimeMillis += delta + } + // Modified flow, accumulate ticks at system time * flow speed + else { + currentTimeMillis += (delta * speed) + } - // Update time with scene objects - scene.accept { element -> - if (element instanceof Temporal) { - element.tick(currentTimeMillis) + // Update time with scene objects + scene.accept { element -> + if (element instanceof Temporal) { + element.tick(currentTimeMillis) + } } - } - lastSystemTimeMillis = currentSystemTimeMillis + lastSystemTimeMillis = currentSystemTimeMillis + } } catch (InterruptedException ignored) { break