diff --git a/src/main/java/com/livingworld/regions/cache/RegionCache.java b/src/main/java/com/livingworld/regions/cache/RegionCache.java
new file mode 100644
index 0000000..f0410b9
--- /dev/null
+++ b/src/main/java/com/livingworld/regions/cache/RegionCache.java
@@ -0,0 +1,105 @@
+package com.livingworld.regions.cache;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Optional;
+
+import com.livingworld.regions.Region;
+import com.livingworld.regions.RegionCoordinate;
+
+/**
+ * In-memory cache for storing and retrieving active regions by their coordinate.
+ *
+ *
This is a plain Java class with no Minecraft dependencies. It provides
+ * thread-safe operations via internal synchronization and returns safe copies
+ * of mutable collections to prevent external modification.
+ */
+public class RegionCache {
+
+ private final HashMap activeRegions;
+
+ /**
+ * Creates a new empty {@code RegionCache}.
+ */
+ public RegionCache() {
+ this.activeRegions = new HashMap<>();
+ }
+
+ // ------------------------------------------------------------------
+ // Query methods
+ // ------------------------------------------------------------------
+
+ /**
+ * Returns the region associated with the given coordinate, or an empty
+ * {@link Optional} if no such region exists in the cache.
+ *
+ * @param coordinate the region coordinate (must not be null)
+ * @return an {@link Optional} containing the region if present
+ * @throws IllegalArgumentException if coordinate is null
+ */
+ public Optional get(RegionCoordinate coordinate) {
+ if (coordinate == null) {
+ throw new IllegalArgumentException("coordinate must not be null");
+ }
+ return Optional.ofNullable(activeRegions.get(coordinate));
+ }
+
+ // ------------------------------------------------------------------
+ // Mutation methods
+ // ------------------------------------------------------------------
+
+ /**
+ * Adds the given region to this cache. If a region with an equal coordinate
+ * already exists, it is replaced.
+ *
+ * @param region the region to add (must not be null)
+ * @throws IllegalArgumentException if region is null or its coordinate is null
+ */
+ public void put(Region region) {
+ if (region == null) {
+ throw new IllegalArgumentException("region must not be null");
+ }
+ RegionCoordinate coordinate = region.getCoordinate();
+ if (coordinate == null) {
+ throw new IllegalArgumentException("region.coordinate must not be null");
+ }
+ activeRegions.put(coordinate, region);
+ }
+
+ /**
+ * Removes the region associated with the given coordinate from this cache.
+ *
+ * @param coordinate the region coordinate (must not be null)
+ * @throws IllegalArgumentException if coordinate is null
+ */
+ public void remove(RegionCoordinate coordinate) {
+ if (coordinate == null) {
+ throw new IllegalArgumentException("coordinate must not be null");
+ }
+ activeRegions.remove(coordinate);
+ }
+
+ // ------------------------------------------------------------------
+ // Aggregate methods
+ // ------------------------------------------------------------------
+
+ /**
+ * Returns a copy of the collection of values in this cache. The returned
+ * collection is unmodifiable; attempting to modify it will throw an
+ * {@link UnsupportedOperationException}.
+ *
+ * @return an unmodifiable copy of all active regions
+ */
+ public Collection allActive() {
+ return new HashMap<>(activeRegions).values();
+ }
+
+ /**
+ * Returns the number of regions currently stored in this cache.
+ *
+ * @return the size of the cache
+ */
+ public int size() {
+ return activeRegions.size();
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/livingworld/regions/query/RegionQueryEngine.java b/src/main/java/com/livingworld/regions/query/RegionQueryEngine.java
new file mode 100644
index 0000000..e25d0b9
--- /dev/null
+++ b/src/main/java/com/livingworld/regions/query/RegionQueryEngine.java
@@ -0,0 +1,84 @@
+package com.livingworld.regions.query;
+
+import java.util.Comparator;
+import java.util.List;
+import java.util.Optional;
+
+import com.livingworld.regions.Region;
+import com.livingworld.regions.RegionCoordinate;
+import com.livingworld.regions.RegionMetrics;
+import com.livingworld.regions.cache.RegionCache;
+
+/**
+ * Runs ecosystem-focused queries against active region data.
+ */
+public final class RegionQueryEngine {
+
+ private static final Comparator BY_COORDINATE =
+ Comparator.comparing((Region region) -> region.getCoordinate().dimensionId())
+ .thenComparingInt(region -> region.getCoordinate().x())
+ .thenComparingInt(region -> region.getCoordinate().z());
+
+ private final RegionCache cache;
+
+ public RegionQueryEngine(RegionCache cache) {
+ if (cache == null) {
+ throw new IllegalArgumentException("cache must not be null");
+ }
+ this.cache = cache;
+ }
+
+ public Optional getRegion(RegionCoordinate coordinate) {
+ if (coordinate == null) {
+ throw new IllegalArgumentException("coordinate must not be null");
+ }
+ return cache.get(coordinate);
+ }
+
+ public List getRegionsInRadius(RegionCoordinate center, int radius) {
+ if (center == null) {
+ throw new IllegalArgumentException("center must not be null");
+ }
+ if (radius < 0) {
+ throw new IllegalArgumentException("radius must be non-negative");
+ }
+
+ return cache.allActive().stream()
+ .filter(region -> region.getCoordinate().dimensionId().equals(center.dimensionId()))
+ .filter(region -> Math.abs((long) region.getCoordinate().x() - center.x()) <= radius)
+ .filter(region -> Math.abs((long) region.getCoordinate().z() - center.z()) <= radius)
+ .sorted(BY_COORDINATE)
+ .toList();
+ }
+
+ public List getRegionsWithActiveEcosystemEvent() {
+ return cache.allActive().stream()
+ .filter(region -> region.getFlags().isHasActiveEcosystemEvent())
+ .sorted(BY_COORDINATE)
+ .toList();
+ }
+
+ public List getRegionsAbovePollution(double threshold) {
+ validateMetricThreshold(threshold);
+ return cache.allActive().stream()
+ .filter(region -> region.getMetrics().getPollutionScore() > threshold)
+ .sorted(BY_COORDINATE)
+ .toList();
+ }
+
+ public List getRegionsBelowSoilQuality(double threshold) {
+ validateMetricThreshold(threshold);
+ return cache.allActive().stream()
+ .filter(region -> region.getMetrics().getSoilQuality() < threshold)
+ .sorted(BY_COORDINATE)
+ .toList();
+ }
+
+ private static void validateMetricThreshold(double threshold) {
+ if (!Double.isFinite(threshold)
+ || threshold < RegionMetrics.MIN_VALUE
+ || threshold > RegionMetrics.MAX_VALUE) {
+ throw new IllegalArgumentException("threshold must be finite and in range [0, 100]");
+ }
+ }
+}
diff --git a/src/test/java/com/livingworld/regions/query/RegionQueryEngineTest.java b/src/test/java/com/livingworld/regions/query/RegionQueryEngineTest.java
new file mode 100644
index 0000000..dec3f00
--- /dev/null
+++ b/src/test/java/com/livingworld/regions/query/RegionQueryEngineTest.java
@@ -0,0 +1,70 @@
+package com.livingworld.regions.query;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import com.livingworld.regions.Region;
+import com.livingworld.regions.RegionCoordinate;
+import com.livingworld.regions.RegionFactory;
+import com.livingworld.regions.cache.RegionCache;
+
+class RegionQueryEngineTest {
+
+ private final RegionFactory factory = new RegionFactory();
+ private final RegionCache cache = new RegionCache();
+ private final RegionQueryEngine queries = new RegionQueryEngine(cache);
+
+ @Test
+ void radiusQueryUsesRegionCoordinatesAndCurrentDimension() {
+ Region center = add("minecraft:overworld", 0, 0);
+ add("minecraft:overworld", 1, 1);
+ add("minecraft:overworld", 2, 0);
+ add("minecraft:the_nether", 0, 0);
+
+ assertEquals(
+ List.of(center.getCoordinate(), new RegionCoordinate("minecraft:overworld", 1, 1)),
+ queries.getRegionsInRadius(center.getCoordinate(), 1).stream()
+ .map(Region::getCoordinate)
+ .toList());
+ }
+
+ @Test
+ void ecosystemQueriesUseFlagsAndMetrics() {
+ Region affected = add("minecraft:overworld", 0, 0);
+ affected.getFlags().setHasActiveEcosystemEvent(true);
+ affected.getMetrics().setPollutionScore(80);
+ affected.getMetrics().setSoilQuality(20);
+
+ Region healthy = add("minecraft:overworld", 1, 0);
+ healthy.getMetrics().setPollutionScore(10);
+ healthy.getMetrics().setSoilQuality(80);
+
+ assertEquals(List.of(affected), queries.getRegionsWithActiveEcosystemEvent());
+ assertEquals(List.of(affected), queries.getRegionsAbovePollution(50));
+ assertEquals(List.of(affected), queries.getRegionsBelowSoilQuality(50));
+ }
+
+ @Test
+ void validatesArguments() {
+ assertThrows(IllegalArgumentException.class,
+ () -> new RegionQueryEngine(null));
+ assertThrows(IllegalArgumentException.class,
+ () -> queries.getRegion(null));
+ assertThrows(IllegalArgumentException.class,
+ () -> queries.getRegionsInRadius(new RegionCoordinate("minecraft:overworld", 0, 0), -1));
+ assertThrows(IllegalArgumentException.class,
+ () -> queries.getRegionsAbovePollution(Double.NaN));
+ assertThrows(IllegalArgumentException.class,
+ () -> queries.getRegionsBelowSoilQuality(101));
+ }
+
+ private Region add(String dimensionId, int x, int z) {
+ Region region = factory.createNewRegion(new RegionCoordinate(dimensionId, x, z), 0);
+ cache.put(region);
+ return region;
+ }
+}