diff --git a/README.md b/README.md
index 2b80fc6..178f7c9 100644
--- a/README.md
+++ b/README.md
@@ -1107,13 +1107,25 @@ No additional configuration required.
#### Purpose and Overview
Provides integration with the Steam platform API through a connector abstraction. Supports Steam features including user identity, Steam overlay, lobbies, achievements, and rich presence. The connector is designed as a singleton and can be optionally initialized when running on Steam.
-**Note**: This is an optional component that requires the Steamworks4J library and proper Steam initialization.
+**Note**: This is an optional component that requires the Steamworks4J library and proper Steam initialization. It is **disabled by default** and must be explicitly enabled via configuration property.
#### Setup / Autoconfiguration
-No auto-configuration provided. Must be manually initialized by setting `SteamworksConnector.setInstance()` with a concrete implementation (e.g., `SteamworksConnectorImpl`).
+**Configuration Class**: `SteamworksAutoConfiguration`
+
+**Property**: `game-engine.steamworks.enabled` (default: `false`)
+
+The auto-configuration is guarded by two conditions:
+- `game-engine.steamworks.enabled=true` must be set
+- The `com.codedisaster.steamworks.SteamAPI` class must be present on the classpath (`@ConditionalOnClass`)
+
+When enabled, `SteamworksConnectorImpl` is instantiated, registered as the singleton via `SteamworksConnector.setInstance()`, and a `SteamworksRunner` thread (implementing `SmartLifecycle`) is started to drive the Steam callback loop. The runner has a phase of `Integer.MAX_VALUE` (starts last, stops first) and will gracefully shut down the Steam API on context close.
#### Exposed Spring Beans
-None (managed as a singleton via static accessor)
+- **`SteamworksConnector`** (via `SteamworksConnectorImpl`): The Steam API connector singleton
+- **`SteamworksRunner`**: Lifecycle-managed callback runner thread
+
+#### Integration Notes
+If a custom `SteamworksConnector` bean is provided, the auto-configuration will not create `SteamworksConnectorImpl` but will still create the `SteamworksRunner` to drive callbacks. Both beans use `@ConditionalOnMissingBean`.
---
diff --git a/pom.xml b/pom.xml
index 5ed8fe2..7e5ac0a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
de.extio
game-engine
- 1.0.24
+ 1.0.25
25
diff --git a/src/main/java/de/extio/game_engine/steamworks/SteamworksAutoConfiguration.java b/src/main/java/de/extio/game_engine/steamworks/SteamworksAutoConfiguration.java
new file mode 100644
index 0000000..d09f861
--- /dev/null
+++ b/src/main/java/de/extio/game_engine/steamworks/SteamworksAutoConfiguration.java
@@ -0,0 +1,28 @@
+package de.extio.game_engine.steamworks;
+
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+
+@AutoConfiguration
+@ConditionalOnProperty(name = "game-engine.steamworks.enabled", havingValue = "true")
+@ConditionalOnClass(name = "com.codedisaster.steamworks.SteamAPI")
+public class SteamworksAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean(SteamworksConnector.class)
+ SteamworksConnectorImpl steamworksConnector() {
+ final var connector = new SteamworksConnectorImpl();
+ SteamworksConnector.setInstance(connector);
+ return connector;
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ SteamworksRunner steamworksRunner(final SteamworksConnector connector) {
+ return new SteamworksRunner(connector);
+ }
+
+}
diff --git a/src/main/java/de/extio/game_engine/steamworks/SteamworksConnectorImpl.java b/src/main/java/de/extio/game_engine/steamworks/SteamworksConnectorImpl.java
index fcb0e37..e7567c4 100644
--- a/src/main/java/de/extio/game_engine/steamworks/SteamworksConnectorImpl.java
+++ b/src/main/java/de/extio/game_engine/steamworks/SteamworksConnectorImpl.java
@@ -1,12 +1,8 @@
package de.extio.game_engine.steamworks;
-import java.util.ArrayList;
-import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.function.Consumer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -17,7 +13,6 @@ import com.codedisaster.steamworks.SteamAuth.AuthSessionResponse;
import com.codedisaster.steamworks.SteamAuthTicket;
import com.codedisaster.steamworks.SteamFriends;
import com.codedisaster.steamworks.SteamFriends.OverlayDialog;
-import com.codedisaster.steamworks.SteamFriends.OverlayToWebPageMode;
import com.codedisaster.steamworks.SteamFriends.PersonaChange;
import com.codedisaster.steamworks.SteamFriendsCallback;
import com.codedisaster.steamworks.SteamID;
@@ -29,18 +24,9 @@ import com.codedisaster.steamworks.SteamMatchmaking.ChatMemberStateChange;
import com.codedisaster.steamworks.SteamMatchmaking.ChatRoomEnterResponse;
import com.codedisaster.steamworks.SteamMatchmaking.LobbyType;
import com.codedisaster.steamworks.SteamMatchmakingCallback;
-import com.codedisaster.steamworks.SteamNativeHandle;
import com.codedisaster.steamworks.SteamPublishedFileID;
-import com.codedisaster.steamworks.SteamRemoteStorage;
-import com.codedisaster.steamworks.SteamRemoteStorage.PublishedFileVisibility;
import com.codedisaster.steamworks.SteamResult;
import com.codedisaster.steamworks.SteamUGC;
-import com.codedisaster.steamworks.SteamUGC.ItemInstallInfo;
-import com.codedisaster.steamworks.SteamUGC.ItemUpdateInfo;
-import com.codedisaster.steamworks.SteamUGC.ItemUpdateStatus;
-import com.codedisaster.steamworks.SteamUGCCallback;
-import com.codedisaster.steamworks.SteamUGCDetails;
-import com.codedisaster.steamworks.SteamUGCQuery;
import com.codedisaster.steamworks.SteamUGCUpdateHandle;
import com.codedisaster.steamworks.SteamUser;
import com.codedisaster.steamworks.SteamUserCallback;
@@ -115,11 +101,6 @@ public final class SteamworksConnectorImpl extends SteamworksConnector {
@Override
public void runCallbacks() {
-
-
- // TODO: Executor thread
-
-
if (!this.active) {
return;
}
diff --git a/src/main/java/de/extio/game_engine/steamworks/SteamworksRunner.java b/src/main/java/de/extio/game_engine/steamworks/SteamworksRunner.java
new file mode 100644
index 0000000..1fc745a
--- /dev/null
+++ b/src/main/java/de/extio/game_engine/steamworks/SteamworksRunner.java
@@ -0,0 +1,93 @@
+package de.extio.game_engine.steamworks;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.context.SmartLifecycle;
+
+public final class SteamworksRunner implements SmartLifecycle {
+
+ private static final Logger LOGGER = LogManager.getLogger();
+
+ private static final int SLEEP_MS = 33;
+
+ private final SteamworksConnector connector;
+
+ private final AtomicBoolean running = new AtomicBoolean();
+
+ private Thread thread;
+
+ public SteamworksRunner(final SteamworksConnector connector) {
+ this.connector = connector;
+ }
+
+ @Override
+ public void start() {
+ if (this.running.compareAndSet(false, true)) {
+ LOGGER.debug(() -> "SteamworksRunner starting");
+
+ this.thread = new Thread(this::run, "SteamworksRunner");
+ this.thread.setDaemon(true);
+ this.thread.start();
+ }
+ }
+
+ @Override
+ public void stop() {
+ if (this.running.compareAndSet(true, false)) {
+ LOGGER.debug(() -> "SteamworksRunner stopping");
+ final Thread t = this.thread;
+ if (t != null) {
+ t.interrupt();
+ try {
+ t.join(3000);
+ }
+ catch (final InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean isRunning() {
+ return this.running.get();
+ }
+
+ @Override
+ public boolean isAutoStartup() {
+ return true;
+ }
+
+ @Override
+ public void stop(final Runnable callback) {
+ this.stop();
+ callback.run();
+ }
+
+ @Override
+ public int getPhase() {
+ return Integer.MAX_VALUE;
+ }
+
+ private void run() {
+ try {
+ while (this.running.get() && this.connector.isActive()) {
+ this.connector.runCallbacks();
+ Thread.sleep(SLEEP_MS);
+ }
+ }
+ catch (final InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ catch (final Throwable t) {
+ LOGGER.warn("Error in SteamworksRunner", t);
+ }
+ finally {
+ this.connector.shutdown();
+ LOGGER.debug(() -> "SteamworksRunner stopped");
+ }
+ }
+
+}
diff --git a/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index 1041b78..6c576bf 100644
--- a/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -8,3 +8,4 @@ de.extio.game_engine.i18n.LocalizationAutoConfiguration
de.extio.game_engine.resource.StaticResourceAutoConfiguration
de.extio.game_engine.audio.AudioAutoConfiguration
de.extio.game_engine.menu.MenuAutoConfiguration
+de.extio.game_engine.steamworks.SteamworksAutoConfiguration