diff --git a/.gitignore b/.gitignore
index 592a652..bfad6e0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,6 +48,8 @@ shelf/
*.ipr
*.iws
misc.xml
+uiDesigner.xml
+
##############################
## Eclipse
@@ -76,6 +78,8 @@ dist/
nbdist/
nbactions.xml
nb-configuration.xml
+misc.xml
+compiler.xml
##############################
## Visual Studio Code
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
deleted file mode 100644
index dcffce8..0000000
--- a/.idea/compiler.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
Provides methods to enable or disable logs globally or per class, with support for specifying
* log levels either via {@link Level} enums or string names.
*/
-// Todo: refactor
public final class Logging {
- /** Disables all logging globally by setting the root logger level to {@link Level#OFF}. */
- public static void disableAllLogs() {
- LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
- Configuration config = ctx.getConfiguration();
- LoggerConfig rootLoggerConfig = config.getRootLogger();
- rootLoggerConfig.setLevel(Level.OFF);
- ctx.updateLoggers();
- }
+ /** Disables all logging globally by setting the root logger level to {@link Level#OFF}. */
+ public static void disableAllLogs() {
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ Configuration config = ctx.getConfiguration();
+ LoggerConfig rootLoggerConfig = config.getRootLogger();
+ rootLoggerConfig.setLevel(Level.OFF);
+ ctx.updateLoggers();
+ }
- /** Enables all logging globally by setting the root logger level to {@link Level#ALL}. */
- public static void enableAllLogs() {
- LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
- Configuration config = ctx.getConfiguration();
- LoggerConfig rootLoggerConfig = config.getRootLogger();
- rootLoggerConfig.setLevel(Level.ALL);
- ctx.updateLoggers();
- }
+ /** Enables all logging globally by setting the root logger level to {@link Level#ALL}. */
+ public static void enableAllLogs() {
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ Configuration config = ctx.getConfiguration();
+ LoggerConfig rootLoggerConfig = config.getRootLogger();
+ rootLoggerConfig.setLevel(Level.ALL);
+ ctx.updateLoggers();
+ }
- /**
- * Enables global logging at a specific level by setting the root logger.
- *
- * @param level the logging level to enable for all logs
- */
- public static void enableAllLogs(Level level) {
- LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
- Configuration config = ctx.getConfiguration();
- LoggerConfig rootLoggerConfig = config.getRootLogger();
- rootLoggerConfig.setLevel(level);
- ctx.updateLoggers();
- }
+ /**
+ * Enables global logging at a specific level by setting the root logger.
+ *
+ * @param level the logging level to enable for all logs
+ */
+ public static void enableAllLogs(Level level) {
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ Configuration config = ctx.getConfiguration();
+ LoggerConfig rootLoggerConfig = config.getRootLogger();
+ rootLoggerConfig.setLevel(level);
+ ctx.updateLoggers();
+ }
- /**
- * Verifies whether the provided string corresponds to a valid class name.
- *
- * @param className fully-qualified class name to check
- * @return true if the class exists, false otherwise
- */
- private static boolean verifyStringIsActualClass(String className) {
- try {
- Class.forName(className);
- return true;
- } catch (ClassNotFoundException e) {
- return false;
- }
- }
+ /**
+ * Verifies whether the provided string corresponds to a valid class name.
+ *
+ * @param className fully-qualified class name to check
+ * @return true if the class exists, false otherwise
+ */
+ private static boolean verifyStringIsActualClass(String className) {
+ try {
+ Class.forName(className);
+ return true;
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
+ }
- /**
- * Internal helper to disable logs for a specific class by name.
- *
- * @param className fully-qualified class name
- */
- private static void disableLogsForClassInternal(String className) {
- LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
- Configuration config = ctx.getConfiguration();
- config.removeLogger(className);
- LoggerConfig specificConfig = new LoggerConfig(className, Level.OFF, false);
- config.addLogger(className, specificConfig);
- ctx.updateLoggers();
- }
+ /**
+ * Internal helper to disable logs for a specific class by name.
+ *
+ * @param className fully-qualified class name
+ */
+ private static void disableLogsForClassInternal(String className) {
+ LoggerContext ctx = (LoggerContext) LogManager.getContext(false);
+ Configuration config = ctx.getConfiguration();
+ config.removeLogger(className);
+ LoggerConfig specificConfig = new LoggerConfig(className, Level.OFF, false);
+ config.addLogger(className, specificConfig);
+ ctx.updateLoggers();
+ }
- /**
- * Disables logs for a specific class.
- *
- * @param class_ the class for which logs should be disabled
- * @param
- * Each generated 64-bit ID encodes:
+ *
+ * Each generated 64-bit ID encodes:
+ *
*
- *
- *
This implementation ensures: + * *
Custom epoch is set to {@code 2025-01-01T00:00:00Z}.
+ *Custom epoch is set to {@code 2025-01-01T00:00:00Z}. + * + *
Usage example: * - *
Usage example:
*{@code
* SnowflakeGenerator generator = new SnowflakeGenerator();
* long id = generator.nextId();
@@ -34,9 +35,7 @@ import java.util.concurrent.atomic.AtomicLong;
*/
public class SnowflakeGenerator {
- /**
- * Custom epoch in milliseconds (2025-01-01T00:00:00Z).
- */
+ /** Custom epoch in milliseconds (2025-01-01T00:00:00Z). */
private static final long EPOCH = Instant.parse("2025-01-01T00:00:00Z").toEpochMilli();
// Bit allocations
@@ -53,17 +52,15 @@ public class SnowflakeGenerator {
private static final long MACHINE_SHIFT = SEQUENCE_BITS;
private static final long TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_BITS;
- /**
- * Unique machine identifier derived from network interfaces (10 bits).
- */
+ /** Unique machine identifier derived from network interfaces (10 bits). */
private static final long machineId = SnowflakeGenerator.genMachineId();
private final AtomicLong lastTimestamp = new AtomicLong(-1L);
private long sequence = 0L;
/**
- * Generates a 10-bit machine identifier based on MAC addresses of network interfaces.
- * Falls back to a random value if MAC cannot be determined.
+ * Generates a 10-bit machine identifier based on MAC addresses of network interfaces. Falls
+ * back to a random value if MAC cannot be determined.
*/
private static long genMachineId() {
try {
@@ -82,6 +79,7 @@ public class SnowflakeGenerator {
/**
* For testing: manually set the last generated timestamp.
+ *
* @param l timestamp in milliseconds
*/
void setTime(long l) {
@@ -89,8 +87,8 @@ public class SnowflakeGenerator {
}
/**
- * Constructs a SnowflakeGenerator.
- * Validates that the machine ID is within allowed range.
+ * Constructs a SnowflakeGenerator. Validates that the machine ID is within allowed range.
+ *
* @throws IllegalArgumentException if machine ID is invalid
*/
public SnowflakeGenerator() {
@@ -102,10 +100,9 @@ public class SnowflakeGenerator {
/**
* Generates the next unique ID.
- *
- * If multiple IDs are generated in the same millisecond, a sequence number
- * is incremented. If the sequence overflows, waits until the next millisecond.
- *
+ *
+ * If multiple IDs are generated in the same millisecond, a sequence number is incremented.
+ * If the sequence overflows, waits until the next millisecond.
*
* @return a unique 64-bit ID
* @throws IllegalStateException if clock moves backwards or timestamp exceeds 41-bit limit
@@ -139,6 +136,7 @@ public class SnowflakeGenerator {
/**
* Waits until the next millisecond if sequence overflows.
+ *
* @param lastTimestamp previous timestamp
* @return new timestamp
*/
@@ -150,9 +148,7 @@ public class SnowflakeGenerator {
return ts;
}
- /**
- * Returns current system timestamp in milliseconds.
- */
+ /** Returns current system timestamp in milliseconds. */
private long timestamp() {
return System.currentTimeMillis();
}
diff --git a/framework/src/main/java/org/toop/framework/asset/types/BundledResource.java b/framework/src/main/java/org/toop/framework/asset/types/BundledResource.java
deleted file mode 100644
index ceb0f5f..0000000
--- a/framework/src/main/java/org/toop/framework/asset/types/BundledResource.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package org.toop.framework.asset.types;
-
-import org.toop.framework.asset.ResourceLoader;
-
-import java.io.File;
-
-/**
- * Represents a resource that can be composed of multiple files, or "bundled" together
- * under a common base name.
- *
- *
Implementing classes allow an {@link ResourceLoader}
- * to automatically merge multiple related files into a single resource instance.
- *
- * Typical use cases include:
- * Implementing classes must provide:
- *Example usage:
- *{@code
- * public class LocalizationAsset extends BaseResource implements BundledResource {
- * private final String baseName;
- *
- * public LocalizationAsset(File file) {
- * super(file);
- * this.baseName = extractBaseName(file.getName());
- * loadFile(file);
- * }
- *
- * @Override
- * public void loadFile(File file) {
- * // merge file into existing bundles
- * }
- *
- * @Override
- * public String getBaseName() {
- * return baseName;
- * }
- * }
- * }
- *
- * When used with an asset loader, all files sharing the same base name are - * automatically merged into a single resource instance.
- */ -public interface BundledResource { - - /** - * Load or merge an additional file into this resource. - * - * @param file the file to load or merge - */ - void loadFile(File file); - - /** - * Return a base name for grouping multiple files into this single resource. - * Files with the same base name are automatically merged by the loader. - * - * @return the base name used to identify this bundled resource - */ - String getBaseName(); - -// /** -// Returns the name -// */ -// String getDefaultName(); -} \ No newline at end of file diff --git a/framework/src/main/java/org/toop/framework/asset/types/FileExtension.java b/framework/src/main/java/org/toop/framework/asset/types/FileExtension.java deleted file mode 100644 index b3c42d5..0000000 --- a/framework/src/main/java/org/toop/framework/asset/types/FileExtension.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.toop.framework.asset.types; - -import org.toop.framework.asset.ResourceLoader; -import org.toop.framework.asset.resources.BaseResource; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; -import java.lang.annotation.ElementType; - -/** - * Annotation to declare which file extensions a {@link BaseResource} subclass - * can handle. - * - *This annotation is processed by the {@link ResourceLoader} - * to automatically register resource types for specific file extensions. - * Each extension listed will be mapped to the annotated resource class, - * allowing the loader to instantiate the correct type when scanning files.
- * - *Usage example:
- *{@code
- * @FileExtension({"png", "jpg"})
- * public class ImageAsset extends BaseResource implements LoadableResource {
- * ...
- * }
- * }
- *
- * Key points:
- *- * Any class implementing {@code LoadableResource} is responsible for managing its own - * loading and unloading logic, such as reading files, initializing data structures, - * or allocating external resources. - *
- * - *Implementing classes must define the following behaviors:
- *Typical usage:
- *{@code
- * public class MyFontAsset extends BaseResource implements LoadableResource {
- * private boolean loaded = false;
- *
- * @Override
- * public void load() {
- * // Load font file into memory
- * loaded = true;
- * }
- *
- * @Override
- * public void unload() {
- * // Release resources if needed
- * loaded = false;
- * }
- *
- * @Override
- * public boolean isLoaded() {
- * return loaded;
- * }
- * }
- * }
- *
- * This interface is commonly used with {@link PreloadResource} to allow automatic - * loading by an {@link ResourceLoader} if desired.
- */ -public interface LoadableResource { - /** - * Load the resource into memory or initialize it. - * This method may throw runtime exceptions if loading fails. - */ - void load(); - - /** - * Unload the resource and free any associated resources. - * After this call, {@link #isLoaded()} should return false. - */ - void unload(); - - /** - * Check whether the resource has been successfully loaded. - * - * @return true if the resource is loaded and ready for use, false otherwise - */ - boolean isLoaded(); -} diff --git a/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java b/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java index 7d256cc..e97574a 100644 --- a/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java +++ b/framework/src/main/java/org/toop/framework/audio/AudioVolumeManager.java @@ -1,12 +1,10 @@ package org.toop.framework.audio; -import com.sun.scenario.Settings; import javafx.scene.media.MediaPlayer; -import org.toop.framework.audio.events.AudioEvents; -import org.toop.framework.eventbus.EventFlow; - import javax.sound.sampled.Clip; import javax.sound.sampled.FloatControl; +import org.toop.framework.audio.events.AudioEvents; +import org.toop.framework.eventbus.EventFlow; public class AudioVolumeManager { private final SoundManager sM; @@ -15,7 +13,7 @@ public class AudioVolumeManager { private double fxVolume = 1.0; private double musicVolume = 1.0; - public AudioVolumeManager(SoundManager soundManager){ + public AudioVolumeManager(SoundManager soundManager) { this.sM = soundManager; new EventFlow() @@ -25,19 +23,22 @@ public class AudioVolumeManager { .listen(this::handleGetCurrentVolume) .listen(this::handleGetCurrentFxVolume) .listen(this::handleGetCurrentMusicVolume); - } - public void updateMusicVolume(MediaPlayer mediaPlayer){ + public void updateMusicVolume(MediaPlayer mediaPlayer) { mediaPlayer.setVolume(this.musicVolume * this.volume); } - public void updateSoundEffectVolume(Clip clip){ - if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)){ - FloatControl volumeControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN); + public void updateSoundEffectVolume(Clip clip) { + if (clip.isControlSupported(FloatControl.Type.MASTER_GAIN)) { + FloatControl volumeControl = + (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN); float min = volumeControl.getMinimum(); float max = volumeControl.getMaximum(); - float dB = (float) (Math.log10(Math.max(this.fxVolume * this.volume, 0.0001)) * 20.0); // convert linear to dB + float dB = + (float) + (Math.log10(Math.max(this.fxVolume * this.volume, 0.0001)) + * 20.0); // convert linear to dB dB = Math.max(min, Math.min(max, dB)); volumeControl.setValue(dB); } @@ -50,7 +51,7 @@ public class AudioVolumeManager { private void handleFxVolumeChange(AudioEvents.ChangeFxVolume event) { this.fxVolume = limitVolume(event.newVolume() / 100); - for (Clip clip : sM.getActiveSoundEffects().values()){ + for (Clip clip : sM.getActiveSoundEffects().values()) { updateSoundEffectVolume(clip); } } @@ -60,32 +61,38 @@ public class AudioVolumeManager { for (MediaPlayer mediaPlayer : sM.getActiveMusic()) { this.updateMusicVolume(mediaPlayer); } - for (Clip clip : sM.getActiveSoundEffects().values()){ + for (Clip clip : sM.getActiveSoundEffects().values()) { updateSoundEffectVolume(clip); } } - private void handleMusicVolumeChange(AudioEvents.ChangeMusicVolume event){ + private void handleMusicVolumeChange(AudioEvents.ChangeMusicVolume event) { this.musicVolume = limitVolume(event.newVolume() / 100); - System.out.println(this.musicVolume); - System.out.println(this.volume); - for (MediaPlayer mediaPlayer : sM.getActiveMusic()){ + for (MediaPlayer mediaPlayer : sM.getActiveMusic()) { this.updateMusicVolume(mediaPlayer); } } private void handleGetCurrentVolume(AudioEvents.GetCurrentVolume event) { - new EventFlow().addPostEvent(new AudioEvents.GetCurrentVolumeResponse(volume * 100, event.snowflakeId())) + new EventFlow() + .addPostEvent( + new AudioEvents.GetCurrentVolumeResponse(volume * 100, event.snowflakeId())) .asyncPostEvent(); } private void handleGetCurrentFxVolume(AudioEvents.GetCurrentFxVolume event) { - new EventFlow().addPostEvent(new AudioEvents.GetCurrentFxVolumeResponse(fxVolume * 100, event.snowflakeId())) + new EventFlow() + .addPostEvent( + new AudioEvents.GetCurrentFxVolumeResponse( + fxVolume * 100, event.snowflakeId())) .asyncPostEvent(); } - private void handleGetCurrentMusicVolume(AudioEvents.GetCurrentMusicVolume event){ - new EventFlow().addPostEvent(new AudioEvents.GetCurrentMusicVolumeResponse(musicVolume * 100, event.snowflakeId())) + private void handleGetCurrentMusicVolume(AudioEvents.GetCurrentMusicVolume event) { + new EventFlow() + .addPostEvent( + new AudioEvents.GetCurrentMusicVolumeResponse( + musicVolume * 100, event.snowflakeId())) .asyncPostEvent(); } } diff --git a/framework/src/main/java/org/toop/framework/audio/SoundManager.java b/framework/src/main/java/org/toop/framework/audio/SoundManager.java index 51c31ad..13a98cb 100644 --- a/framework/src/main/java/org/toop/framework/audio/SoundManager.java +++ b/framework/src/main/java/org/toop/framework/audio/SoundManager.java @@ -1,20 +1,18 @@ package org.toop.framework.audio; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.toop.framework.SnowflakeGenerator; -import org.toop.framework.asset.ResourceManager; -import org.toop.framework.asset.ResourceMeta; -import org.toop.framework.asset.resources.MusicAsset; -import org.toop.framework.asset.resources.SoundEffectAsset; -import org.toop.framework.audio.events.AudioEvents; -import org.toop.framework.eventbus.EventFlow; - -import javafx.scene.media.MediaPlayer; - import java.io.*; import java.util.*; +import javafx.scene.media.MediaPlayer; import javax.sound.sampled.*; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.toop.framework.SnowflakeGenerator; +import org.toop.framework.audio.events.AudioEvents; +import org.toop.framework.eventbus.EventFlow; +import org.toop.framework.resource.ResourceManager; +import org.toop.framework.resource.ResourceMeta; +import org.toop.framework.resource.resources.MusicAsset; +import org.toop.framework.resource.resources.SoundEffectAsset; public class SoundManager { private static final Logger logger = LogManager.getLogger(SoundManager.class); @@ -22,13 +20,12 @@ public class SoundManager { private final Queue- * The {@code ResourceLoader} scans a root folder recursively, identifies files, - * and maps them to registered resource types based on file extensions and - * {@link FileExtension} annotations. - * It supports multiple resource types including {@link PreloadResource} (automatically loaded) - * and {@link BundledResource} (merged across multiple files). - *
* - *Assets are stored in a static, thread-safe list and can be retrieved - * through {@link ResourceManager}.
+ *The {@code ResourceLoader} scans a root folder recursively, identifies files, and maps them to + * registered resource types based on file extensions and {@link FileExtension} annotations. It + * supports multiple resource types including {@link PreloadResource} (automatically loaded) and + * {@link BundledResource} (merged across multiple files). + * + *
Assets are stored in a static, thread-safe list and can be retrieved through {@link + * ResourceManager}. + * + *
Features: * - *
Features:
*Usage example:
+ *Usage example: + * *
{@code
* ResourceLoader loader = new ResourceLoader("assets");
* double progress = loader.getProgress();
@@ -48,14 +48,17 @@ import java.util.function.Function;
*/
public class ResourceLoader {
private static final Logger logger = LogManager.getLogger(ResourceLoader.class);
- private static final List> assets = new CopyOnWriteArrayList<>();
- private final Map> registry = new ConcurrentHashMap<>();
+ private static final List> assets =
+ new CopyOnWriteArrayList<>();
+ private final Map> registry =
+ new ConcurrentHashMap<>();
private final AtomicInteger loadedCount = new AtomicInteger(0);
private int totalCount = 0;
/**
* Constructs an ResourceLoader and loads assets from the given root folder.
+ *
* @param rootFolder the folder containing asset files
*/
public ResourceLoader(File rootFolder) {
@@ -84,6 +87,7 @@ public class ResourceLoader {
/**
* Constructs an ResourceLoader from a folder path.
+ *
* @param rootFolder the folder path containing assets
*/
public ResourceLoader(String rootFolder) {
@@ -92,6 +96,7 @@ public class ResourceLoader {
/**
* Returns the current progress of loading assets (0.0 to 1.0).
+ *
* @return progress as a double
*/
public double getProgress() {
@@ -100,6 +105,7 @@ public class ResourceLoader {
/**
* Returns the number of assets loaded so far.
+ *
* @return loaded count
*/
public int getLoadedCount() {
@@ -108,6 +114,7 @@ public class ResourceLoader {
/**
* Returns the total number of files found to load.
+ *
* @return total asset count
*/
public int getTotalCount() {
@@ -116,6 +123,7 @@ public class ResourceLoader {
/**
* Returns a snapshot list of all assets loaded by this loader.
+ *
* @return list of loaded assets
*/
public List> getAssets() {
@@ -124,6 +132,7 @@ public class ResourceLoader {
/**
* Registers a factory for a specific file extension.
+ *
* @param extension the file extension (without dot)
* @param factory a function mapping a File to a resource instance
* @param the type of resource
@@ -132,69 +141,79 @@ public class ResourceLoader {
this.registry.put(extension, factory);
}
- /**
- * Maps a file to a resource instance based on its extension and registered factories.
- */
- private T resourceMapper(File file, Class type) {
+ /** Maps a file to a resource instance based on its extension and registered factories. */
+ private T resourceMapper(File file)
+ throws CouldNotCreateResourceFactoryException, IllegalArgumentException {
String ext = getExtension(file.getName());
Function factory = registry.get(ext);
- if (factory == null) return null;
+ if (factory == null)
+ throw new CouldNotCreateResourceFactoryException(registry, file.getName());
BaseResource resource = factory.apply(file);
- if (!type.isInstance(resource)) {
+ if (resource == null) {
throw new IllegalArgumentException(
- "File " + file.getName() + " is not of type " + type.getSimpleName()
- );
+ "File "
+ + file.getName()
+ + " is not of type "
+ + BaseResource.class.getSimpleName());
}
- return type.cast(resource);
+ return ((Class) BaseResource.class).cast(resource);
}
- /**
- * Loads the given list of files into assets, handling bundled and preload resources.
- */
+ /** Loads the given list of files into assets, handling bundled and preload resources. */
private void loader(List files) {
Map bundledResources = new HashMap<>();
for (File file : files) {
boolean skipAdd = false;
- BaseResource resource = resourceMapper(file, BaseResource.class);
+ BaseResource resource = null;
+ try {
+ resource = resourceMapper(file);
+ } catch (CouldNotCreateResourceFactoryException _) {
+ logger.warn("Could not create resource for: {}", file);
+ } catch (IllegalArgumentException e) {
+ logger.error(e);
+ }
switch (resource) {
case null -> {
continue;
}
case BundledResource br -> {
String key = resource.getClass().getName() + ":" + br.getBaseName();
- if (!bundledResources.containsKey(key)) {bundledResources.put(key, br);}
+ if (!bundledResources.containsKey(key)) {
+ bundledResources.put(key, br);
+ }
bundledResources.get(key).loadFile(file);
resource = (BaseResource) bundledResources.get(key);
assets.add(new ResourceMeta<>(br.getBaseName(), resource));
skipAdd = true;
}
case PreloadResource pr -> pr.load();
- default -> {
- }
+ default -> {}
}
BaseResource finalResource = resource;
- boolean alreadyAdded = assets.stream()
- .anyMatch(a -> a.getResource() == finalResource);
+ boolean alreadyAdded = assets.stream().anyMatch(a -> a.getResource() == finalResource);
if (!alreadyAdded && !skipAdd) {
assets.add(new ResourceMeta<>(file.getName(), resource));
}
- logger.info("Loaded {} from {}", resource.getClass().getSimpleName(), file.getAbsolutePath());
+ logger.info(
+ "Loaded {} from {}",
+ resource.getClass().getSimpleName(),
+ file.getAbsolutePath());
loadedCount.incrementAndGet();
new EventFlow()
- .addPostEvent(new AssetLoaderEvents.LoadingProgressUpdate(loadedCount.get(), totalCount))
+ .addPostEvent(
+ new AssetLoaderEvents.LoadingProgressUpdate(
+ loadedCount.get(), totalCount))
.postEvent();
}
}
- /**
- * Recursively searches a folder and adds all files to the foundFiles list.
- */
+ /** Recursively searches a folder and adds all files to the foundFiles list. */
private void fileSearcher(final File folder, List foundFiles) {
for (File fileEntry : Objects.requireNonNull(folder.listFiles())) {
if (fileEntry.isDirectory()) {
@@ -206,31 +225,31 @@ public class ResourceLoader {
}
/**
- * Uses reflection to automatically register all {@link BaseResource} subclasses
- * annotated with {@link FileExtension}.
+ * Uses reflection to automatically register all {@link BaseResource} subclasses annotated with
+ * {@link FileExtension}.
*/
private void autoRegisterResources() {
- Reflections reflections = new Reflections("org.toop.framework.asset.resources");
+ Reflections reflections = new Reflections("org.toop.framework.resource.resources");
Set> classes = reflections.getSubTypesOf(BaseResource.class);
for (Class extends BaseResource> cls : classes) {
if (!cls.isAnnotationPresent(FileExtension.class)) continue;
FileExtension annotation = cls.getAnnotation(FileExtension.class);
for (String ext : annotation.value()) {
- registry.put(ext, file -> {
- try {
- return cls.getConstructor(File.class).newInstance(file);
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- });
+ registry.put(
+ ext,
+ file -> {
+ try {
+ return cls.getConstructor(File.class).newInstance(file);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
}
}
}
- /**
- * Extracts the base name from a file name, used for bundling multiple files.
- */
+ /** Extracts the base name from a file name, used for bundling multiple files. */
private static String getBaseName(String fileName) {
int underscoreIndex = fileName.indexOf('_');
int dotIndex = fileName.lastIndexOf('.');
@@ -238,9 +257,7 @@ public class ResourceLoader {
return fileName.substring(0, dotIndex);
}
- /**
- * Returns the file extension of a given file name (without dot).
- */
+ /** Returns the file extension of a given file name (without dot). */
public static String getExtension(String name) {
int i = name.lastIndexOf('.');
return (i > 0) ? name.substring(i + 1) : "";
diff --git a/framework/src/main/java/org/toop/framework/asset/ResourceManager.java b/framework/src/main/java/org/toop/framework/resource/ResourceManager.java
similarity index 61%
rename from framework/src/main/java/org/toop/framework/asset/ResourceManager.java
rename to framework/src/main/java/org/toop/framework/resource/ResourceManager.java
index 983dc02..9641733 100644
--- a/framework/src/main/java/org/toop/framework/asset/ResourceManager.java
+++ b/framework/src/main/java/org/toop/framework/resource/ResourceManager.java
@@ -1,30 +1,31 @@
-package org.toop.framework.asset;
-
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.toop.framework.asset.resources.*;
+package org.toop.framework.resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.toop.framework.resource.exceptions.ResourceNotFoundException;
+import org.toop.framework.resource.resources.*;
+
/**
* Centralized manager for all loaded assets in the application.
- *
- * {@code ResourceManager} maintains a thread-safe registry of {@link Asset} objects
- * and provides utility methods to retrieve assets by name, ID, or type.
- * It works together with {@link ResourceLoader} to register assets automatically
- * when they are loaded from the file system.
- *
*
- * Key responsibilities:
+ * {@code ResourceManager} maintains a thread-safe registry of {@link ResourceMeta} objects and
+ * provides utility methods to retrieve assets by name, ID, or type. It works together with {@link
+ * ResourceLoader} to register assets automatically when they are loaded from the file system.
+ *
+ *
Key responsibilities:
+ *
*
- * - Storing all loaded assets in a concurrent map.
- * - Providing typed access to asset resources.
- * - Allowing lookup by asset name or ID.
- * - Supporting retrieval of all assets of a specific {@link BaseResource} subclass.
+ * - Storing all loaded assets in a concurrent map.
+ *
- Providing typed access to asset resources.
+ *
- Allowing lookup by asset name or ID.
+ *
- Supporting retrieval of all assets of a specific {@link BaseResource} subclass.
*
*
- * Example usage:
+ * Example usage:
+ *
*
{@code
* // Load assets from a loader
* ResourceLoader loader = new ResourceLoader(new File("RootFolder"));
@@ -40,36 +41,29 @@ import java.util.concurrent.ConcurrentHashMap;
* Optional> maybeAsset = ResourceManager.findByName("menu.css");
* }
*
- * Notes:
+ * Notes:
+ *
*
- * - All retrieval methods are static and thread-safe.
- * - The {@link #get(String)} method may require casting if the asset type is not known at compile time.
- * - Assets should be loaded via {@link ResourceLoader} before retrieval.
+ * - All retrieval methods are static and thread-safe.
+ *
- The {@link #get(String)} method may require casting if the asset type is not known at
+ * compile time.
+ *
- Assets should be loaded via {@link ResourceLoader} before retrieval.
*
*/
public class ResourceManager {
- private static final Logger logger = LogManager.getLogger(ResourceManager.class);
- private static final ResourceManager INSTANCE = new ResourceManager();
- private static final Map> assets = new ConcurrentHashMap<>();
+ private static final Logger logger = LogManager.getLogger(ResourceManager.class);
+ private static final Map> assets =
+ new ConcurrentHashMap<>();
private ResourceManager() {}
- /**
- * Returns the singleton instance of {@code ResourceManager}.
- *
- * @return the shared instance
- */
- public static ResourceManager getInstance() {
- return INSTANCE;
- }
-
/**
* Loads all assets from a given {@link ResourceLoader} into the manager.
*
* @param loader the loader that has already loaded assets
*/
- public synchronized static void loadAssets(ResourceLoader loader) {
- for (var asset : loader.getAssets()) {
+ public static synchronized void loadAssets(ResourceLoader loader) {
+ for (ResourceMeta extends BaseResource> asset : loader.getAssets()) {
assets.put(asset.getName(), asset);
}
}
@@ -85,15 +79,15 @@ public class ResourceManager {
public static T get(String name) {
ResourceMeta asset = (ResourceMeta) assets.get(name);
if (asset == null) {
- throw new TypeNotPresentException(name, new RuntimeException(String.format("Type %s not present", name))); // TODO: Create own exception, BAM
+ throw new ResourceNotFoundException(name);
}
return asset.getResource();
}
-// @SuppressWarnings("unchecked")
-// public static ArrayList> getAllOfType() {
-// return (ArrayList>) (ArrayList>) new ArrayList<>(assets.values());
-// }
+ // @SuppressWarnings("unchecked")
+ // public static ArrayList> getAllOfType() {
+ // return (ArrayList>) (ArrayList>) new ArrayList<>(assets.values());
+ // }
/**
* Retrieve all assets of a specific resource type.
@@ -136,5 +130,6 @@ public class ResourceManager {
*/
public static void addAsset(ResourceMeta extends BaseResource> asset) {
assets.put(asset.getName(), asset);
+ logger.info("Successfully added asset: {}, to the asset list", asset.getName());
}
}
diff --git a/framework/src/main/java/org/toop/framework/asset/ResourceMeta.java b/framework/src/main/java/org/toop/framework/resource/ResourceMeta.java
similarity index 85%
rename from framework/src/main/java/org/toop/framework/asset/ResourceMeta.java
rename to framework/src/main/java/org/toop/framework/resource/ResourceMeta.java
index 972b635..4312b84 100644
--- a/framework/src/main/java/org/toop/framework/asset/ResourceMeta.java
+++ b/framework/src/main/java/org/toop/framework/resource/ResourceMeta.java
@@ -1,7 +1,7 @@
-package org.toop.framework.asset;
+package org.toop.framework.resource;
import org.toop.framework.SnowflakeGenerator;
-import org.toop.framework.asset.resources.BaseResource;
+import org.toop.framework.resource.resources.BaseResource;
public class ResourceMeta {
private final Long id;
@@ -25,5 +25,4 @@ public class ResourceMeta {
public T getResource() {
return this.resource;
}
-
}
diff --git a/framework/src/main/java/org/toop/framework/asset/events/AssetLoaderEvents.java b/framework/src/main/java/org/toop/framework/resource/events/AssetLoaderEvents.java
similarity index 60%
rename from framework/src/main/java/org/toop/framework/asset/events/AssetLoaderEvents.java
rename to framework/src/main/java/org/toop/framework/resource/events/AssetLoaderEvents.java
index 91a296e..04ef018 100644
--- a/framework/src/main/java/org/toop/framework/asset/events/AssetLoaderEvents.java
+++ b/framework/src/main/java/org/toop/framework/resource/events/AssetLoaderEvents.java
@@ -1,7 +1,8 @@
-package org.toop.framework.asset.events;
+package org.toop.framework.resource.events;
import org.toop.framework.eventbus.events.EventWithoutSnowflake;
public class AssetLoaderEvents {
- public record LoadingProgressUpdate(int hasLoadedAmount, int isLoadingAmount) implements EventWithoutSnowflake {}
+ public record LoadingProgressUpdate(int hasLoadedAmount, int isLoadingAmount)
+ implements EventWithoutSnowflake {}
}
diff --git a/framework/src/main/java/org/toop/framework/resource/exceptions/CouldNotCreateResourceFactoryException.java b/framework/src/main/java/org/toop/framework/resource/exceptions/CouldNotCreateResourceFactoryException.java
new file mode 100644
index 0000000..aa7b3c0
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/resource/exceptions/CouldNotCreateResourceFactoryException.java
@@ -0,0 +1,12 @@
+package org.toop.framework.resource.exceptions;
+
+import java.util.Map;
+
+public class CouldNotCreateResourceFactoryException extends RuntimeException {
+ public CouldNotCreateResourceFactoryException(Map, ?> registry, String fileName) {
+ super(
+ String.format(
+ "Could not create resource factory for: %s, isRegistryEmpty: %b",
+ fileName, registry.isEmpty()));
+ }
+}
diff --git a/framework/src/main/java/org/toop/framework/resource/exceptions/IsNotAResourceException.java b/framework/src/main/java/org/toop/framework/resource/exceptions/IsNotAResourceException.java
new file mode 100644
index 0000000..41abbde
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/resource/exceptions/IsNotAResourceException.java
@@ -0,0 +1,7 @@
+package org.toop.framework.resource.exceptions;
+
+public class IsNotAResourceException extends RuntimeException {
+ public IsNotAResourceException(Class clazz, String message) {
+ super(clazz.getName() + " does not implement BaseResource");
+ }
+}
diff --git a/framework/src/main/java/org/toop/framework/resource/exceptions/ResourceNotFoundException.java b/framework/src/main/java/org/toop/framework/resource/exceptions/ResourceNotFoundException.java
new file mode 100644
index 0000000..c75de1e
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/resource/exceptions/ResourceNotFoundException.java
@@ -0,0 +1,7 @@
+package org.toop.framework.resource.exceptions;
+
+public class ResourceNotFoundException extends RuntimeException {
+ public ResourceNotFoundException(String name) {
+ super("Could not find resource: " + name);
+ }
+}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/BaseResource.java b/framework/src/main/java/org/toop/framework/resource/resources/BaseResource.java
similarity index 84%
rename from framework/src/main/java/org/toop/framework/asset/resources/BaseResource.java
rename to framework/src/main/java/org/toop/framework/resource/resources/BaseResource.java
index c1aa040..72da47c 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/BaseResource.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/BaseResource.java
@@ -1,4 +1,4 @@
-package org.toop.framework.asset.resources;
+package org.toop.framework.resource.resources;
import java.io.*;
@@ -14,5 +14,4 @@ public abstract class BaseResource {
public File getFile() {
return this.file;
}
-
}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/CssAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/CssAsset.java
similarity index 73%
rename from framework/src/main/java/org/toop/framework/asset/resources/CssAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/CssAsset.java
index 6f8cd19..0d056ae 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/CssAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/CssAsset.java
@@ -1,8 +1,7 @@
-package org.toop.framework.asset.resources;
-
-import org.toop.framework.asset.types.FileExtension;
+package org.toop.framework.resource.resources;
import java.io.File;
+import org.toop.framework.resource.types.FileExtension;
@FileExtension({"css"})
public class CssAsset extends BaseResource {
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/FontAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/FontAsset.java
similarity index 91%
rename from framework/src/main/java/org/toop/framework/asset/resources/FontAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/FontAsset.java
index 1054d03..da9c709 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/FontAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/FontAsset.java
@@ -1,12 +1,11 @@
-package org.toop.framework.asset.resources;
-
-import javafx.scene.text.Font;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.PreloadResource;
+package org.toop.framework.resource.resources;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
+import javafx.scene.text.Font;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.PreloadResource;
@FileExtension({"ttf", "otf"})
public class FontAsset extends BaseResource implements PreloadResource {
@@ -60,4 +59,4 @@ public class FontAsset extends BaseResource implements PreloadResource {
}
return this.family;
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/ImageAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/ImageAsset.java
similarity index 86%
rename from framework/src/main/java/org/toop/framework/asset/resources/ImageAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/ImageAsset.java
index ed2c87e..2e6b417 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/ImageAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/ImageAsset.java
@@ -1,11 +1,10 @@
-package org.toop.framework.asset.resources;
-
-import javafx.scene.image.Image;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
+package org.toop.framework.resource.resources;
import java.io.File;
import java.io.FileInputStream;
+import javafx.scene.image.Image;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.LoadableResource;
@FileExtension({"png", "jpg", "jpeg"})
public class ImageAsset extends BaseResource implements LoadableResource {
@@ -45,4 +44,4 @@ public class ImageAsset extends BaseResource implements LoadableResource {
}
return null;
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/JsonAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/JsonAsset.java
similarity index 83%
rename from framework/src/main/java/org/toop/framework/asset/resources/JsonAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/JsonAsset.java
index 5f9e1ba..c13849c 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/JsonAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/JsonAsset.java
@@ -1,12 +1,13 @@
-package org.toop.framework.asset.resources;
+package org.toop.framework.resource.resources;
+
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.LoadableResource;
@FileExtension({"json"})
public class JsonAsset extends BaseResource implements LoadableResource {
@@ -25,7 +26,8 @@ public class JsonAsset extends BaseResource implements LoadableResource {
File file = getFile();
if (!file.exists()) {
try {
- // make a new file with the declared constructor (example: settings) if it doesn't exist
+ // make a new file with the declared constructor (example: settings) if it doesn't
+ // exist
content = type.getDeclaredConstructor().newInstance();
save();
} catch (Exception e) {
@@ -36,7 +38,7 @@ public class JsonAsset extends BaseResource implements LoadableResource {
try (FileReader reader = new FileReader(file)) {
content = gson.fromJson(reader, type);
this.isLoaded = true;
- } catch(Exception e) {
+ } catch (Exception e) {
throw new RuntimeException("Failed to load JSON asset" + getFile(), e);
}
}
@@ -59,10 +61,11 @@ public class JsonAsset extends BaseResource implements LoadableResource {
File file = getFile();
File parent = file.getParentFile();
if (parent != null && !parent.exists()) {
- parent.mkdirs();
+ boolean isDirectoryMade = parent.mkdirs();
+ assert isDirectoryMade;
}
try (FileWriter writer = new FileWriter(file)) {
- gson.toJson(content, writer);
+ gson.toJson(content, writer);
} catch (IOException e) {
throw new RuntimeException("Failed to save JSON asset" + getFile(), e);
}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/LocalizationAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/LocalizationAsset.java
similarity index 64%
rename from framework/src/main/java/org/toop/framework/asset/resources/LocalizationAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/LocalizationAsset.java
index cf18f14..2bd2333 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/LocalizationAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/LocalizationAsset.java
@@ -1,34 +1,29 @@
-package org.toop.framework.asset.resources;
-
-import org.toop.framework.asset.types.BundledResource;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
+package org.toop.framework.resource.resources;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
+import org.toop.framework.resource.types.BundledResource;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.LoadableResource;
/**
- * Represents a localization resource asset that loads and manages property files
- * containing key-value pairs for different locales.
- *
- * This class implements {@link LoadableResource} to support loading/unloading
- * and {@link BundledResource} to represent resources that can contain multiple
- * localized bundles.
- *
- *
- * Files handled by this class must have the {@code .properties} extension,
- * optionally with a locale suffix, e.g., {@code messages_en_US.properties}.
- *
+ * Represents a localization resource asset that loads and manages property files containing
+ * key-value pairs for different locales.
+ *
+ * This class implements {@link LoadableResource} to support loading/unloading and {@link
+ * BundledResource} to represent resources that can contain multiple localized bundles.
+ *
+ *
Files handled by this class must have the {@code .properties} extension, optionally with a
+ * locale suffix, e.g., {@code messages_en_US.properties}.
+ *
+ *
Example usage:
*
- *
- * Example usage:
*
{@code
* LocalizationAsset asset = new LocalizationAsset(new File("messages_en_US.properties"));
* asset.load();
* String greeting = asset.getString("hello", Locale.US);
* }
- *
*/
@FileExtension({"properties"})
public class LocalizationAsset extends BaseResource implements LoadableResource, BundledResource {
@@ -43,7 +38,7 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
private final String baseName = "localization";
/** Fallback locale used when no matching locale is found. */
- private final Locale fallback = Locale.forLanguageTag("en_US");
+ private final Locale fallback = Locale.forLanguageTag("en");
/**
* Constructs a new LocalizationAsset for the specified file.
@@ -54,18 +49,14 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
super(file);
}
- /**
- * Loads the resource file into memory and prepares localized bundles.
- */
+ /** Loads the resource file into memory and prepares localized bundles. */
@Override
public void load() {
loadFile(getFile());
isLoaded = true;
}
- /**
- * Unloads all loaded resource bundles, freeing memory.
- */
+ /** Unloads all loaded resource bundles, freeing memory. */
@Override
public void unload() {
bundles.clear();
@@ -83,11 +74,10 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
}
/**
- * Retrieves a localized string for the given key and locale.
- * If an exact match for the locale is not found, a fallback
- * matching the language or the default locale will be used.
+ * Retrieves a localized string for the given key and locale. If an exact match for the locale
+ * is not found, a fallback matching the language or the default locale will be used.
*
- * @param key the key of the string
+ * @param key the key of the string
* @param locale the desired locale
* @return the localized string
* @throws MissingResourceException if no resource bundle is available for the locale
@@ -95,14 +85,15 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
public String getString(String key, Locale locale) {
Locale target = findBestLocale(locale);
ResourceBundle bundle = bundles.get(target);
- if (bundle == null) throw new MissingResourceException(
- "No bundle for locale: " + target, getClass().getName(), key);
+ if (bundle == null)
+ throw new MissingResourceException(
+ "No bundle for locale: " + target, getClass().getName(), key);
return bundle.getString(key);
}
/**
- * Finds the best matching locale among loaded bundles.
- * Prefers an exact match, then language-only match, then fallback.
+ * Finds the best matching locale among loaded bundles. Prefers an exact match, then
+ * language-only match, then fallback.
*
* @param locale the desired locale
* @return the best matching locale
@@ -125,8 +116,8 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
}
/**
- * Loads a specific property file as a resource bundle.
- * The locale is extracted from the file name if present.
+ * Loads a specific property file as a resource bundle. The locale is extracted from the file
+ * name if present.
*
* @param file the property file to load
* @throws RuntimeException if the file cannot be read
@@ -134,7 +125,7 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
@Override
public void loadFile(File file) {
try (InputStreamReader reader =
- new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) {
+ new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8)) {
Locale locale = extractLocale(file.getName(), baseName);
bundles.put(locale, new PropertyResourceBundle(reader));
} catch (IOException e) {
@@ -153,22 +144,23 @@ public class LocalizationAsset extends BaseResource implements LoadableResource,
return this.baseName;
}
-// /**
-// * Extracts the base name from a file name.
-// *
-// * @param fileName the file name
-// * @return base name without locale or extension
-// */
-// private String getBaseName(String fileName) {
-// int dotIndex = fileName.lastIndexOf('.');
-// String nameWithoutExtension = (dotIndex > 0) ? fileName.substring(0, dotIndex) : fileName;
-//
-// int underscoreIndex = nameWithoutExtension.indexOf('_');
-// if (underscoreIndex > 0) {
-// return nameWithoutExtension.substring(0, underscoreIndex);
-// }
-// return nameWithoutExtension;
-// }
+ // /**
+ // * Extracts the base name from a file name.
+ // *
+ // * @param fileName the file name
+ // * @return base name without locale or extension
+ // */
+ // private String getBaseName(String fileName) {
+ // int dotIndex = fileName.lastIndexOf('.');
+ // String nameWithoutExtension = (dotIndex > 0) ? fileName.substring(0, dotIndex) :
+ // fileName;
+ //
+ // int underscoreIndex = nameWithoutExtension.indexOf('_');
+ // if (underscoreIndex > 0) {
+ // return nameWithoutExtension.substring(0, underscoreIndex);
+ // }
+ // return nameWithoutExtension;
+ // }
/**
* Extracts a locale from a file name based on the pattern "base_LOCALE.properties".
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/MusicAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/MusicAsset.java
similarity index 81%
rename from framework/src/main/java/org/toop/framework/asset/resources/MusicAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/MusicAsset.java
index fc6eec7..d60b6bc 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/MusicAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/MusicAsset.java
@@ -1,10 +1,9 @@
-package org.toop.framework.asset.resources;
-
-import javafx.scene.media.Media;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
+package org.toop.framework.resource.resources;
import java.io.*;
+import javafx.scene.media.Media;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.LoadableResource;
@FileExtension({"mp3"})
public class MusicAsset extends BaseResource implements LoadableResource {
@@ -37,4 +36,4 @@ public class MusicAsset extends BaseResource implements LoadableResource {
public boolean isLoaded() {
return isLoaded;
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/SettingsAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/SettingsAsset.java
similarity index 74%
rename from framework/src/main/java/org/toop/framework/asset/resources/SettingsAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/SettingsAsset.java
index c496213..7728c9a 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/SettingsAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/SettingsAsset.java
@@ -1,10 +1,8 @@
-package org.toop.framework.asset.resources;
-
-
-import org.toop.framework.settings.Settings;
+package org.toop.framework.resource.resources;
import java.io.File;
import java.util.Locale;
+import org.toop.framework.settings.Settings;
public class SettingsAsset extends JsonAsset {
@@ -32,13 +30,13 @@ public class SettingsAsset extends JsonAsset {
return getContent().fullScreen;
}
- public String getTheme() {
- return getContent().theme;
- }
+ public String getTheme() {
+ return getContent().theme;
+ }
- public String getLayoutSize() {
- return getContent().layoutSize;
- }
+ public String getLayoutSize() {
+ return getContent().layoutSize;
+ }
public void setVolume(int volume) {
getContent().volume = volume;
@@ -65,13 +63,13 @@ public class SettingsAsset extends JsonAsset {
save();
}
- public void setTheme(String theme) {
- getContent().theme = theme;
- save();
- }
+ public void setTheme(String theme) {
+ getContent().theme = theme;
+ save();
+ }
- public void setLayoutSize(String layoutSize) {
- getContent().layoutSize = layoutSize;
- save();
- }
-}
\ No newline at end of file
+ public void setLayoutSize(String layoutSize) {
+ getContent().layoutSize = layoutSize;
+ save();
+ }
+}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/SoundEffectAsset.java
similarity index 58%
rename from framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/SoundEffectAsset.java
index d207077..c55306a 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/SoundEffectAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/SoundEffectAsset.java
@@ -1,11 +1,10 @@
-package org.toop.framework.asset.resources;
+package org.toop.framework.resource.resources;
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
-
-import javax.sound.sampled.*;
import java.io.*;
import java.nio.file.Files;
+import javax.sound.sampled.*;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.LoadableResource;
@FileExtension({"wav"})
public class SoundEffectAsset extends BaseResource implements LoadableResource {
@@ -16,22 +15,25 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource {
}
// Gets a new clip to play
- public Clip getNewClip() throws LineUnavailableException, UnsupportedAudioFileException, IOException {
+ public Clip getNewClip()
+ throws LineUnavailableException, UnsupportedAudioFileException, IOException {
// Get a new clip from audio system
Clip clip = AudioSystem.getClip();
// Insert a new audio stream into the clip
AudioInputStream inputStream = this.getAudioStream();
AudioFormat baseFormat = inputStream.getFormat();
- if (baseFormat.getSampleSizeInBits() > 16) inputStream = downSampleAudio(inputStream, baseFormat);
- clip.open(inputStream); // ^ Clip can only run 16 bit and lower, thus downsampling necessary.
+ if (baseFormat.getSampleSizeInBits() > 16)
+ inputStream = downSampleAudio(inputStream, baseFormat);
+ clip.open(
+ inputStream); // ^ Clip can only run 16 bit and lower, thus downsampling necessary.
return clip;
}
// Generates a new audio stream from byte array
private AudioInputStream getAudioStream() throws UnsupportedAudioFileException, IOException {
// Check if raw data is loaded into memory
- if(!this.isLoaded()){
+ if (!this.isLoaded()) {
this.load();
}
@@ -39,16 +41,18 @@ public class SoundEffectAsset extends BaseResource implements LoadableResource {
return AudioSystem.getAudioInputStream(new ByteArrayInputStream(this.rawData));
}
- private AudioInputStream downSampleAudio(AudioInputStream audioInputStream, AudioFormat baseFormat) {
- AudioFormat decodedFormat = new AudioFormat(
- AudioFormat.Encoding.PCM_SIGNED,
- baseFormat.getSampleRate(),
- 16, // force 16-bit
- baseFormat.getChannels(),
- baseFormat.getChannels() * 2,
- baseFormat.getSampleRate(),
- false // little-endian
- );
+ private AudioInputStream downSampleAudio(
+ AudioInputStream audioInputStream, AudioFormat baseFormat) {
+ AudioFormat decodedFormat =
+ new AudioFormat(
+ AudioFormat.Encoding.PCM_SIGNED,
+ baseFormat.getSampleRate(),
+ 16, // force 16-bit
+ baseFormat.getChannels(),
+ baseFormat.getChannels() * 2,
+ baseFormat.getSampleRate(),
+ false // little-endian
+ );
return AudioSystem.getAudioInputStream(decodedFormat, audioInputStream);
}
diff --git a/framework/src/main/java/org/toop/framework/asset/resources/TextAsset.java b/framework/src/main/java/org/toop/framework/resource/resources/TextAsset.java
similarity index 85%
rename from framework/src/main/java/org/toop/framework/asset/resources/TextAsset.java
rename to framework/src/main/java/org/toop/framework/resource/resources/TextAsset.java
index e6acc3c..9d91405 100644
--- a/framework/src/main/java/org/toop/framework/asset/resources/TextAsset.java
+++ b/framework/src/main/java/org/toop/framework/resource/resources/TextAsset.java
@@ -1,12 +1,11 @@
-package org.toop.framework.asset.resources;
-
-import org.toop.framework.asset.types.FileExtension;
-import org.toop.framework.asset.types.LoadableResource;
+package org.toop.framework.resource.resources;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
+import org.toop.framework.resource.types.FileExtension;
+import org.toop.framework.resource.types.LoadableResource;
@FileExtension({"txt", "json", "xml"})
public class TextAsset extends BaseResource implements LoadableResource {
@@ -41,4 +40,4 @@ public class TextAsset extends BaseResource implements LoadableResource {
public String getContent() {
return this.content;
}
-}
\ No newline at end of file
+}
diff --git a/framework/src/main/java/org/toop/framework/resource/types/BundledResource.java b/framework/src/main/java/org/toop/framework/resource/types/BundledResource.java
new file mode 100644
index 0000000..ca63407
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/resource/types/BundledResource.java
@@ -0,0 +1,77 @@
+package org.toop.framework.resource.types;
+
+import java.io.File;
+import org.toop.framework.resource.ResourceLoader;
+
+/**
+ * Represents a resource that can be composed of multiple files, or "bundled" together under a
+ * common base name.
+ *
+ * Implementing classes allow an {@link ResourceLoader} to automatically merge multiple related
+ * files into a single resource instance.
+ *
+ *
Typical use cases include:
+ *
+ *
+ * - Localization assets, where multiple `.properties` files (e.g., `messages_en.properties`,
+ * `messages_nl.properties`) are grouped under the same logical resource.
+ *
- Sprite sheets, tile sets, or other multi-file resources that logically belong together.
+ *
+ *
+ * Implementing classes must provide:
+ *
+ *
+ * - {@link #loadFile(File)}: Logic to load or merge an individual file into the resource.
+ *
- {@link #getBaseName()}: A consistent base name used to group multiple files into this
+ * resource.
+ *
+ *
+ * Example usage:
+ *
+ *
{@code
+ * public class LocalizationAsset extends BaseResource implements BundledResource {
+ * private final String baseName;
+ *
+ * public LocalizationAsset(File file) {
+ * super(file);
+ * this.baseName = extractBaseName(file.getName());
+ * loadFile(file);
+ * }
+ *
+ * @Override
+ * public void loadFile(File file) {
+ * // merge file into existing bundles
+ * }
+ *
+ * @Override
+ * public String getBaseName() {
+ * return baseName;
+ * }
+ * }
+ * }
+ *
+ * When used with an asset loader, all files sharing the same base name are automatically merged
+ * into a single resource instance.
+ */
+public interface BundledResource {
+
+ /**
+ * Load or merge an additional file into this resource.
+ *
+ * @param file the file to load or merge
+ */
+ void loadFile(File file);
+
+ /**
+ * Return a base name for grouping multiple files into this single resource. Files with the same
+ * base name are automatically merged by the loader.
+ *
+ * @return the base name used to identify this bundled resource
+ */
+ String getBaseName();
+
+ // /**
+ // Returns the name
+ // */
+ // String getDefaultName();
+}
diff --git a/framework/src/main/java/org/toop/framework/resource/types/FileExtension.java b/framework/src/main/java/org/toop/framework/resource/types/FileExtension.java
new file mode 100644
index 0000000..df30efd
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/resource/types/FileExtension.java
@@ -0,0 +1,44 @@
+package org.toop.framework.resource.types;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.toop.framework.resource.ResourceLoader;
+import org.toop.framework.resource.resources.BaseResource;
+
+/**
+ * Annotation to declare which file extensions a {@link BaseResource} subclass can handle.
+ *
+ *
This annotation is processed by the {@link ResourceLoader} to automatically register resource
+ * types for specific file extensions. Each extension listed will be mapped to the annotated
+ * resource class, allowing the loader to instantiate the correct type when scanning files.
+ *
+ *
Usage example:
+ *
+ *
{@code
+ * @FileExtension({"png", "jpg"})
+ * public class ImageAsset extends BaseResource implements LoadableResource {
+ * ...
+ * }
+ * }
+ *
+ * Key points:
+ *
+ *
+ * - The annotation is retained at runtime for reflection-based registration.
+ *
- Can only be applied to types (classes) that extend {@link BaseResource}.
+ *
- Multiple extensions can be specified in the {@code value()} array.
+ *
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface FileExtension {
+ /**
+ * The list of file extensions (without leading dot) that the annotated resource class can
+ * handle.
+ *
+ * @return array of file extensions
+ */
+ String[] value();
+}
diff --git a/framework/src/main/java/org/toop/framework/resource/types/LoadableResource.java b/framework/src/main/java/org/toop/framework/resource/types/LoadableResource.java
new file mode 100644
index 0000000..32fb0bc
--- /dev/null
+++ b/framework/src/main/java/org/toop/framework/resource/types/LoadableResource.java
@@ -0,0 +1,69 @@
+package org.toop.framework.resource.types;
+
+import org.toop.framework.resource.ResourceLoader;
+
+/**
+ * Represents a resource that can be explicitly loaded and unloaded.
+ *
+ * Any class implementing {@code LoadableResource} is responsible for managing its own loading
+ * and unloading logic, such as reading files, initializing data structures, or allocating external
+ * resources.
+ *
+ *
Implementing classes must define the following behaviors:
+ *
+ *
+ * - {@link #load()}: Load the resource into memory or perform necessary initialization.
+ *
- {@link #unload()}: Release any held resources or memory when the resource is no longer
+ * needed.
+ *
- {@link #isLoaded()}: Return {@code true} if the resource has been successfully loaded and
+ * is ready for use, {@code false} otherwise.
+ *
+ *
+ * Typical usage:
+ *
+ *
{@code
+ * public class MyFontAsset extends BaseResource implements LoadableResource {
+ * private boolean loaded = false;
+ *
+ * @Override
+ * public void load() {
+ * // Load font file into memory
+ * loaded = true;
+ * }
+ *
+ * @Override
+ * public void unload() {
+ * // Release resources if needed
+ * loaded = false;
+ * }
+ *
+ * @Override
+ * public boolean isLoaded() {
+ * return loaded;
+ * }
+ * }
+ * }
+ *
+ * This interface is commonly used with {@link PreloadResource} to allow automatic loading by an
+ * {@link ResourceLoader} if desired.
+ */
+public interface LoadableResource {
+ /**
+ * Load the resource into memory or initialize it. This method may throw runtime exceptions if
+ * loading fails.
+ */
+ void load();
+
+ /**
+ * Unload the resource and free any associated resources. After this call, {@link #isLoaded()}
+ * should return false.
+ */
+ void unload();
+
+ /**
+ * Check whether the resource has been successfully loaded.
+ *
+ * @return true if the resource is loaded and ready for use, false otherwise
+ */
+ boolean isLoaded();
+}
diff --git a/framework/src/main/java/org/toop/framework/asset/types/PreloadResource.java b/framework/src/main/java/org/toop/framework/resource/types/PreloadResource.java
similarity index 67%
rename from framework/src/main/java/org/toop/framework/asset/types/PreloadResource.java
rename to framework/src/main/java/org/toop/framework/resource/types/PreloadResource.java
index 07d213d..28f8734 100644
--- a/framework/src/main/java/org/toop/framework/asset/types/PreloadResource.java
+++ b/framework/src/main/java/org/toop/framework/resource/types/PreloadResource.java
@@ -1,19 +1,21 @@
-package org.toop.framework.asset.types;
+package org.toop.framework.resource.types;
-import org.toop.framework.asset.ResourceLoader;
+import org.toop.framework.resource.ResourceLoader;
/**
- * Marker interface for resources that should be **automatically loaded** by the {@link ResourceLoader}.
+ * Marker interface for resources that should be **automatically loaded** by the {@link
+ * ResourceLoader}.
*
- *
Extends {@link LoadableResource}, so any implementing class must provide the standard
- * {@link LoadableResource#load()} and {@link LoadableResource#unload()} methods, as well as the
- * {@link LoadableResource#isLoaded()} check.
+ * Extends {@link LoadableResource}, so any implementing class must provide the standard {@link
+ * LoadableResource#load()} and {@link LoadableResource#unload()} methods, as well as the {@link
+ * LoadableResource#isLoaded()} check.
*
*
When a resource implements {@code PreloadResource}, the {@code ResourceLoader} will invoke
* {@link LoadableResource#load()} automatically after the resource is discovered and instantiated,
- * without requiring manual loading by the user.
+ * without requiring manual loading by the user.
+ *
+ * Typical usage:
*
- *
Typical usage:
* {@code
* public class MyFontAsset extends BaseResource implements PreloadResource {
* @Override
@@ -34,6 +36,6 @@ import org.toop.framework.asset.ResourceLoader;
* }
*
* Note: Only use this interface for resources that are safe to load at startup, as it may
- * increase memory usage or startup time.
+ * increase memory usage or startup time.
*/
public interface PreloadResource extends LoadableResource {}
diff --git a/framework/src/main/java/org/toop/framework/settings/Settings.java b/framework/src/main/java/org/toop/framework/settings/Settings.java
index b1ed334..052107c 100644
--- a/framework/src/main/java/org/toop/framework/settings/Settings.java
+++ b/framework/src/main/java/org/toop/framework/settings/Settings.java
@@ -3,8 +3,8 @@ package org.toop.framework.settings;
public class Settings {
public boolean fullScreen = false;
public String locale = "en";
- public String theme = "dark";
- public String layoutSize = "medium";
+ public String theme = "dark";
+ public String layoutSize = "medium";
public int volume = 100;
public int fxVolume = 20;
public int musicVolume = 15;
diff --git a/framework/src/test/java/org/toop/framework/networking/events/NetworkEventsTest.java b/framework/src/test/java/org/toop/framework/networking/events/NetworkEventsTest.java
index 3641549..f2a129d 100644
--- a/framework/src/test/java/org/toop/framework/networking/events/NetworkEventsTest.java
+++ b/framework/src/test/java/org/toop/framework/networking/events/NetworkEventsTest.java
@@ -35,10 +35,10 @@ class NetworkEventsTest {
@Test
void testChallengeResponse() {
NetworkEvents.ChallengeResponse event =
- new NetworkEvents.ChallengeResponse(1L, "Alice", "Chess", "ch001");
- assertEquals("Alice", event.challengerName());
- assertEquals("Chess", event.gameType());
- assertEquals("ch001", event.challengeId());
+ new NetworkEvents.ChallengeResponse(1L, "John", "1", "tic-tac-toe");
+ assertEquals("John", event.challengerName());
+ assertEquals("1", event.challengeId());
+ assertEquals("tic-tac-toe", event.gameType());
}
@Test
diff --git a/game/src/main/java/org/toop/game/Game.java b/game/src/main/java/org/toop/game/Game.java
index 71b2214..9b4259c 100644
--- a/game/src/main/java/org/toop/game/Game.java
+++ b/game/src/main/java/org/toop/game/Game.java
@@ -3,34 +3,37 @@ package org.toop.game;
import java.util.Arrays;
public abstract class Game {
- public enum State {
- NORMAL, DRAW, WIN,
- }
+ public enum State {
+ NORMAL,
+ DRAW,
+ WIN,
+ }
- public record Move(int position, char value) {}
+ public record Move(int position, char value) {}
- public static final char EMPTY = (char)0;
+ public static final char EMPTY = (char) 0;
- public final int rowSize;
- public final int columnSize;
- public final char[] board;
+ public final int rowSize;
+ public final int columnSize;
+ public final char[] board;
- protected Game(int rowSize, int columnSize) {
- assert rowSize > 0 && columnSize > 0;
+ protected Game(int rowSize, int columnSize) {
+ assert rowSize > 0 && columnSize > 0;
- this.rowSize = rowSize;
- this.columnSize = columnSize;
+ this.rowSize = rowSize;
+ this.columnSize = columnSize;
- board = new char[rowSize * columnSize];
- Arrays.fill(board, EMPTY);
- }
+ board = new char[rowSize * columnSize];
+ Arrays.fill(board, EMPTY);
+ }
- protected Game(Game other) {
- rowSize = other.rowSize;
- columnSize = other.columnSize;
- board = Arrays.copyOf(other.board, other.board.length);
- }
+ protected Game(Game other) {
+ rowSize = other.rowSize;
+ columnSize = other.columnSize;
+ board = Arrays.copyOf(other.board, other.board.length);
+ }
- public abstract Move[] getLegalMoves();
- public abstract State play(Move move);
-}
\ No newline at end of file
+ public abstract Move[] getLegalMoves();
+
+ public abstract State play(Move move);
+}
diff --git a/game/src/main/java/org/toop/game/TurnBasedGame.java b/game/src/main/java/org/toop/game/TurnBasedGame.java
index 2da6337..b4eb1d3 100644
--- a/game/src/main/java/org/toop/game/TurnBasedGame.java
+++ b/game/src/main/java/org/toop/game/TurnBasedGame.java
@@ -1,25 +1,27 @@
package org.toop.game;
public abstract class TurnBasedGame extends Game {
- public final int turns;
+ public final int turns;
- protected int currentTurn;
+ protected int currentTurn;
- protected TurnBasedGame(int rowSize, int columnSize, int turns) {
- super(rowSize, columnSize);
+ protected TurnBasedGame(int rowSize, int columnSize, int turns) {
+ super(rowSize, columnSize);
assert turns >= 2;
this.turns = turns;
- }
+ }
- protected TurnBasedGame(TurnBasedGame other) {
- super(other);
- turns = other.turns;
- currentTurn = other.currentTurn;
- }
+ protected TurnBasedGame(TurnBasedGame other) {
+ super(other);
+ turns = other.turns;
+ currentTurn = other.currentTurn;
+ }
- protected void nextTurn() {
- currentTurn = (currentTurn + 1) % turns;
- }
+ protected void nextTurn() {
+ currentTurn = (currentTurn + 1) % turns;
+ }
- public int getCurrentTurn() { return currentTurn; }
-}
\ No newline at end of file
+ public int getCurrentTurn() {
+ return currentTurn;
+ }
+}
diff --git a/game/src/main/java/org/toop/game/othello/Othello.java b/game/src/main/java/org/toop/game/othello/Othello.java
index 3012eac..435527a 100644
--- a/game/src/main/java/org/toop/game/othello/Othello.java
+++ b/game/src/main/java/org/toop/game/othello/Othello.java
@@ -3,17 +3,17 @@ package org.toop.game.othello;
import org.toop.game.TurnBasedGame;
public final class Othello extends TurnBasedGame {
- Othello() {
- super(8, 8, 2);
- }
+ Othello() {
+ super(8, 8, 2);
+ }
- @Override
- public Move[] getLegalMoves() {
- return new Move[0];
- }
+ @Override
+ public Move[] getLegalMoves() {
+ return new Move[0];
+ }
- @Override
- public State play(Move move) {
- return null;
- }
-}
\ No newline at end of file
+ @Override
+ public State play(Move move) {
+ return null;
+ }
+}
diff --git a/game/src/main/java/org/toop/game/othello/OthelloAI.java b/game/src/main/java/org/toop/game/othello/OthelloAI.java
index 8957387..40f147c 100644
--- a/game/src/main/java/org/toop/game/othello/OthelloAI.java
+++ b/game/src/main/java/org/toop/game/othello/OthelloAI.java
@@ -4,8 +4,8 @@ import org.toop.game.AI;
import org.toop.game.Game;
public final class OthelloAI extends AI {
- @Override
- public Game.Move findBestMove(Othello game, int depth) {
- return null;
- }
+ @Override
+ public Game.Move findBestMove(Othello game, int depth) {
+ return null;
+ }
}
diff --git a/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java b/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java
index ee2a5b9..0fa6ca8 100644
--- a/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java
+++ b/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java
@@ -1,8 +1,7 @@
package org.toop.game.tictactoe;
-import org.toop.game.TurnBasedGame;
-
import java.util.ArrayList;
+import org.toop.game.TurnBasedGame;
public final class TicTacToe extends TurnBasedGame {
private int movesLeft;
@@ -19,8 +18,8 @@ public final class TicTacToe extends TurnBasedGame {
@Override
public Move[] getLegalMoves() {
- final ArrayList legalMoves = new ArrayList<>();
- final char currentValue = getCurrentValue();
+ final ArrayList legalMoves = new ArrayList<>();
+ final char currentValue = getCurrentValue();
for (int i = 0; i < board.length; i++) {
if (board[i] == EMPTY) {
@@ -44,12 +43,12 @@ public final class TicTacToe extends TurnBasedGame {
return State.WIN;
}
- nextTurn();
+ nextTurn();
if (movesLeft <= 2) {
- if (movesLeft <= 0 || checkForEarlyDraw(this)) {
- return State.DRAW;
- }
+ if (movesLeft <= 0 || checkForEarlyDraw(this)) {
+ return State.DRAW;
+ }
}
return State.NORMAL;
@@ -60,7 +59,9 @@ public final class TicTacToe extends TurnBasedGame {
for (int i = 0; i < 3; i++) {
final int index = i * 3;
- if (board[index] != EMPTY && board[index] == board[index + 1] && board[index] == board[index + 2]) {
+ if (board[index] != EMPTY
+ && board[index] == board[index + 1]
+ && board[index] == board[index + 2]) {
return true;
}
}
@@ -83,7 +84,7 @@ public final class TicTacToe extends TurnBasedGame {
private boolean checkForEarlyDraw(TicTacToe game) {
for (final Move move : game.getLegalMoves()) {
- final TicTacToe copy = new TicTacToe(game);
+ final TicTacToe copy = new TicTacToe(game);
if (copy.play(move) == State.WIN || !checkForEarlyDraw(copy)) {
return false;
@@ -93,7 +94,7 @@ public final class TicTacToe extends TurnBasedGame {
return true;
}
- private char getCurrentValue() {
- return currentTurn == 0? 'X' : 'O';
- }
-}
\ No newline at end of file
+ private char getCurrentValue() {
+ return currentTurn == 0 ? 'X' : 'O';
+ }
+}
diff --git a/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java b/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java
index 6be92f5..325b8ee 100644
--- a/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java
+++ b/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java
@@ -1,83 +1,81 @@
package org.toop.game.tictactoe;
-import org.toop.game.Game;
-
-import java.util.Set;
-
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.*;
+import java.util.Set;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.toop.game.Game;
+
class TicTacToeAITest {
- private TicTacToe game;
- private TicTacToeAI ai;
+ private TicTacToe game;
+ private TicTacToeAI ai;
- @BeforeEach
- void setup() {
- game = new TicTacToe();
- ai = new TicTacToeAI();
- }
+ @BeforeEach
+ void setup() {
+ game = new TicTacToe();
+ ai = new TicTacToeAI();
+ }
- @Test
- void testBestMove_returnWinningMoveWithDepth1() {
- // X X -
- // O O -
- // - - -
- game.play(new Game.Move(0, 'X'));
- game.play(new Game.Move(3, 'O'));
- game.play(new Game.Move(1, 'X'));
- game.play(new Game.Move(4, 'O'));
+ @Test
+ void testBestMove_returnWinningMoveWithDepth1() {
+ // X X -
+ // O O -
+ // - - -
+ game.play(new Game.Move(0, 'X'));
+ game.play(new Game.Move(3, 'O'));
+ game.play(new Game.Move(1, 'X'));
+ game.play(new Game.Move(4, 'O'));
- final Game.Move move = ai.findBestMove(game, 1);
+ final Game.Move move = ai.findBestMove(game, 1);
- assertNotNull(move);
- assertEquals('X', move.value());
- assertEquals(2, move.position());
- }
+ assertNotNull(move);
+ assertEquals('X', move.value());
+ assertEquals(2, move.position());
+ }
- @Test
- void testBestMove_blockOpponentWinDepth1() {
- // - - -
- // O - -
- // X X -
- game.play(new Game.Move(6, 'X'));
- game.play(new Game.Move(3, 'O'));
- game.play(new Game.Move(7, 'X'));
+ @Test
+ void testBestMove_blockOpponentWinDepth1() {
+ // - - -
+ // O - -
+ // X X -
+ game.play(new Game.Move(6, 'X'));
+ game.play(new Game.Move(3, 'O'));
+ game.play(new Game.Move(7, 'X'));
- final Game.Move move = ai.findBestMove(game, 1);
+ final Game.Move move = ai.findBestMove(game, 1);
- assertNotNull(move);
- assertEquals('O', move.value());
- assertEquals(8, move.position());
- }
+ assertNotNull(move);
+ assertEquals('O', move.value());
+ assertEquals(8, move.position());
+ }
- @Test
- void testBestMove_preferCornerOnEmpty() {
- final Game.Move move = ai.findBestMove(game, 0);
+ @Test
+ void testBestMove_preferCornerOnEmpty() {
+ final Game.Move move = ai.findBestMove(game, 0);
- assertNotNull(move);
- assertEquals('X', move.value());
- assertTrue(Set.of(0, 2, 6, 8).contains(move.position()));
- }
+ assertNotNull(move);
+ assertEquals('X', move.value());
+ assertTrue(Set.of(0, 2, 6, 8).contains(move.position()));
+ }
- @Test
- void testBestMove_findBestMoveDraw() {
- // O X -
- // - O X
- // X O X
- game.play(new Game.Move(1, 'X'));
- game.play(new Game.Move(0, 'O'));
- game.play(new Game.Move(5, 'X'));
- game.play(new Game.Move(4, 'O'));
- game.play(new Game.Move(6, 'X'));
- game.play(new Game.Move(7, 'O'));
- game.play(new Game.Move(8, 'X'));
+ @Test
+ void testBestMove_findBestMoveDraw() {
+ // O X -
+ // - O X
+ // X O X
+ game.play(new Game.Move(1, 'X'));
+ game.play(new Game.Move(0, 'O'));
+ game.play(new Game.Move(5, 'X'));
+ game.play(new Game.Move(4, 'O'));
+ game.play(new Game.Move(6, 'X'));
+ game.play(new Game.Move(7, 'O'));
+ game.play(new Game.Move(8, 'X'));
- final Game.Move move = ai.findBestMove(game, game.getLegalMoves().length);
+ final Game.Move move = ai.findBestMove(game, game.getLegalMoves().length);
- assertNotNull(move);
- assertEquals('O', move.value());
- assertEquals(2, move.position());
- }
-}
\ No newline at end of file
+ assertNotNull(move);
+ assertEquals('O', move.value());
+ assertEquals(2, move.position());
+ }
+}