Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/dependencies.sha256
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
4e69696892b88b41c55d49ab2fdcc21eead92bf54acc588c0050596c3b75199c libs/json-simple-1.1.1.jar
2cbd119bf1961c28788310963dc80ba65f58cdeec1dd139c8bdb1240faa2c36f libs/gson-2.14.0.jar
5a01b37bd7442fb0f2c809cadac82ba70f75173956e04efac3336595cc070368 libs/ItemsAdder-4.0.18.jar
44ee292392dd86b4aa4824c2e11668a9dee0333b83ae013987e1ba78d19f51a0 libs/ModelEngine-R4.1.1.jar
c571502e768640c7cff10c49b3eef904ccf3fd95bd81803913abfe43cfc09746 libs/NBTAPI-2.16.1.jar
7 changes: 7 additions & 0 deletions .github/scripts/install-local-dependencies.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,10 @@ mvn -B --no-transfer-progress org.apache.maven.plugins:maven-install-plugin:3.1.
mvn -B --no-transfer-progress org.apache.maven.plugins:maven-install-plugin:3.1.4:install-file \
-Dfile="libs/ItemsAdder-4.0.18.jar" -DgroupId="local" -DartifactId="ItemsAdder" \
-Dversion="4.0.18-tfmc-5a01b37bd744" -Dpackaging=jar -DgeneratePom=true "$@"

mvn -B --no-transfer-progress org.apache.maven.plugins:maven-install-plugin:3.1.4:install-file \
-Dfile="libs/NBTAPI-2.16.1.jar" -DgroupId="local" -DartifactId="item-nbt-api-plugin" \
-Dversion="2.16.1-tfmc-c571502e7686" -Dpackaging=jar -DgeneratePom=true "$@"
mvn -B --no-transfer-progress org.apache.maven.plugins:maven-install-plugin:3.1.4:install-file \
-Dfile="libs/ModelEngine-R4.1.1.jar" -DgroupId="local" -DartifactId="ModelEngine" \
-Dversion="R4.1.1-tfmc-44ee292392dd" -Dpackaging=jar -DgeneratePom=true "$@"
2 changes: 2 additions & 0 deletions .github/scripts/prepare-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ mkdir -p libs
curl --fail --location --silent --show-error --retry 3 -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github.raw+json" "https://api.github.com/repos/TF-Minecraft/ServerAssets/contents/jars/4e69696892b8/json-simple-1.1.1.jar?ref=$ref" > "libs/json-simple-1.1.1.jar"
curl --fail --location --silent --show-error --retry 3 -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github.raw+json" "https://api.github.com/repos/TF-Minecraft/ServerAssets/contents/jars/2cbd119bf196/gson-2.14.0.jar?ref=$ref" > "libs/gson-2.14.0.jar"
curl --fail --location --silent --show-error --retry 3 -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github.raw+json" "https://api.github.com/repos/TF-Minecraft/ServerAssets/contents/jars/5a01b37bd744/ItemsAdder-4.0.18.jar?ref=$ref" > "libs/ItemsAdder-4.0.18.jar"
curl --fail --location --silent --show-error --retry 3 -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github.raw+json" "https://api.github.com/repos/TF-Minecraft/ServerAssets/contents/jars/44ee292392dd/ModelEngine-R4.1.1.jar?ref=$ref" > "libs/ModelEngine-R4.1.1.jar"
curl --fail --location --silent --show-error --retry 3 -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github.raw+json" "https://api.github.com/repos/TF-Minecraft/ServerAssets/contents/jars/c571502e7686/NBTAPI-2.16.1.jar?ref=$ref" > "libs/NBTAPI-2.16.1.jar"
bash .github/scripts/install-local-dependencies.sh "$@"
8 changes: 8 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,11 @@ jobs:
name: unit-test-reports-${{ github.run_id }}-${{ github.run_attempt }}
path: target/surefire-reports/
if-no-files-found: error

- name: Upload coverage reports
if: ${{ !cancelled() && hashFiles('target/site/jacoco/jacoco.xml') != '' }}
uses: actions/upload-artifact@v7
with:
name: coverage-reports-${{ github.run_id }}-${{ github.run_attempt }}
path: target/site/jacoco/
if-no-files-found: error
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,14 @@ Copyright (c) 2026 TF-Minecraft contributors.
TF-Minecraft-authored material in this repository is licensed under the
[Artistic License 2.0](LICENSE). Third-party dependencies and bundled material
retain their own licenses.

## Tests and coverage

Run `mvn clean verify` with Java 21 after preparing the pinned dependencies.
The build enforces 100% production line, branch and instruction coverage with
JaCoCo, without exclusions. CI uploads the HTML and XML coverage reports.

The tests cover construction state, material accounting, menus, permissions,
placement timers, reloads, persistence, displays and plugin lifecycle. Bukkit
and external plugin boundaries use MockBukkit/Mockito; configuration and item
metadata use real serialization. Live Minecraft integration remains separate.
94 changes: 94 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,56 @@
<maven.compiler.release>21</maven.compiler.release>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.proc>full</maven.compiler.proc>
<coreprotect.version>25.0.0</coreprotect.version>
<tlibs.version>2.0.0</tlibs.version>
<vehicleframework.version>2.0.1</vehicleframework.version>
</properties>

<dependencies>
<dependency>
<groupId>local</groupId>
<artifactId>ModelEngine</artifactId>
<version>R4.1.1-tfmc-44ee292392dd</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>it.unimi.dsi</groupId>
<artifactId>fastutil</artifactId>
<version>8.5.15</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>local</groupId>
<artifactId>item-nbt-api-plugin</artifactId>
<version>2.16.1-tfmc-c571502e7686</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.coreprotect</groupId>
<artifactId>CoreProtect</artifactId>
<version>${coreprotect.version}</version>
<!-- VehicleFramework's public API references CoreProtect; also needed by integration mocks. -->
<scope>provided</scope>
<exclusions><exclusion><groupId>*</groupId><artifactId>*</artifactId></exclusion></exclusions>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.20.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockbukkit.mockbukkit</groupId>
<artifactId>mockbukkit-v1.21</artifactId>
<version>4.95.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>local</groupId>
<artifactId>json-simple</artifactId>
Expand Down Expand Up @@ -93,6 +138,55 @@
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.5.4</version>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.15</version>
<executions>
<execution>
<goals><goal>prepare-agent</goal></goals>
</execution>
<execution>
<id>coverage-report</id>
<phase>verify</phase>
<goals><goal>report</goal></goals>
</execution>
<execution>
<id>coverage-check</id>
<phase>verify</phase>
<goals><goal>check</goal></goals>
<configuration>
<rules>
<rule>
<element>BUNDLE</element>
<limits>
<limit>
<counter>INSTRUCTION</counter>
<value>COVEREDRATIO</value>
<minimum>1.0</minimum>
</limit>
<limit>
<counter>LINE</counter>
<value>COVEREDRATIO</value>
<minimum>1.0</minimum>
</limit>
<limit>
<counter>BRANCH</counter>
<value>COVEREDRATIO</value>
<minimum>1.0</minimum>
</limit>
</limits>
</rule>
</rules>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/net/tfminecraft/vfbuilders/VFBuilders.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ public void loadConfigs() {
}
VFLogger.info(this, "Loading blueprints...");
for (File file : files) {
if (file != null && file.isFile()) {
if (file.isFile()) {
blueprintLoader.load(file);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,7 @@ public static TextDisplay spawn(World world, Location stationBlock, String text)
return null;
}
Location anchor = displayAnchor(stationBlock);
if (anchor == null) {
return null;
}
String key = blockKey(stationBlock);
if (key == null) {
return null;
}
return world.spawn(anchor, TextDisplay.class, display -> apply(display, anchor, text, key));
}

Expand All @@ -82,7 +76,7 @@ public static void update(TextDisplay display, Location stationBlock, String tex
return;
}
Location anchor = displayAnchor(stationBlock);
if (anchor == null || anchor.getWorld() == null) {
if (anchor.getWorld() == null) {
return;
}
display.teleport(anchor);
Expand Down Expand Up @@ -122,13 +116,7 @@ public static void purgeAtStationBlock(Location stationBlock, double radius) {
return;
}
String key = blockKey(stationBlock);
if (key == null) {
return;
}
Location anchor = displayAnchor(stationBlock);
if (anchor == null) {
return;
}
World world = stationBlock.getWorld();
int chunkX = stationBlock.getBlockX() >> 4;
int chunkZ = stationBlock.getBlockZ() >> 4;
Expand Down Expand Up @@ -158,9 +146,6 @@ public static void purgeLegacyArmorStands(Location stationBlock, double radius)
return;
}
Location anchor = displayAnchor(stationBlock);
if (anchor == null) {
return;
}
World world = stationBlock.getWorld();
int chunkX = stationBlock.getBlockX() >> 4;
int chunkZ = stationBlock.getBlockZ() >> 4;
Expand Down Expand Up @@ -205,9 +190,6 @@ private static boolean hasStationTimerTag(Entity entity) {
}

private static String getStationTimerTag(Entity entity) {
if (entity == null) {
return null;
}
PersistentDataContainer container = entity.getPersistentDataContainer();
return container.get(stationTimerKey(), PersistentDataType.STRING);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ private ItemStack getCategoryItem(Player p, BlueprintCategory cat) {
ItemMeta m = i.getItemMeta();
NamespacedKey key = new NamespacedKey(VFBuilders.plugin, "vfb_category_id");
m.getPersistentDataContainer().set(key, PersistentDataType.STRING, cat.getId());
List<String> lore = new ArrayList<>(m.getLore());
List<String> lore = m.hasLore() ? new ArrayList<>(m.getLore()) : new ArrayList<>();
lore.add(" ");
lore.add(StringFormatter.formatHex("#53db9c"+getBlueprintCount(p, cat)+" #ccbf8fBlueprints"));
m.setLore(lore);
Expand All @@ -124,7 +124,7 @@ private ItemStack getBlueprintItem(Blueprint b) {
m.setDisplayName(v.getName());
NamespacedKey key = new NamespacedKey(VFBuilders.plugin, "vfb_blueprint_id");
m.getPersistentDataContainer().set(key, PersistentDataType.STRING, b.getId());
List<String> lore = new ArrayList<>(m.getLore());
List<String> lore = m.hasLore() ? new ArrayList<>(m.getLore()) : new ArrayList<>();
lore.add(" ");
lore.add(StringFormatter.formatHex("#ccbf8fSeats§e: #86d672"+v.getSeatHandler().getSeats().size()));
if(v.getWeapons().size() > 0) lore.add(StringFormatter.formatHex("#ccbf8fWeapons§e: #86d672"+v.getWeapons().size()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,6 @@ public void onChunkUnload(ChunkUnloadEvent e) {

private Iterable<ActiveStation> stationsInChunk(Chunk chunk) {
List<ActiveStation> inChunk = new ArrayList<>();
if (chunk == null) {
return inChunk;
}
int chunkX = chunk.getX();
int chunkZ = chunk.getZ();
for (ActiveStation station : stations.values()) {
Expand Down Expand Up @@ -247,7 +244,8 @@ public void onPlayerLeftClick(PlayerInteractEvent e) {
Location stationLoc = placement.getStation().getLocation();
Location clickLoc = p.getLocation();

if (clickLoc.distanceSquared(stationLoc) > Math.pow(Cache.constructionDistance, 2)) {
if (!java.util.Objects.equals(clickLoc.getWorld(), stationLoc.getWorld())
|| clickLoc.distanceSquared(stationLoc) > Math.pow(Cache.constructionDistance, 2)) {
p.sendMessage("§cToo far from the station! (max "+Cache.constructionDistance+" blocks)");
return;
}
Expand Down Expand Up @@ -295,15 +293,16 @@ private void startParticleTrail(ActivePlacement placement) {
new BukkitRunnable() {
@Override
public void run() {
if (!activePlacements.containsKey(uuid)) {
if (activePlacements.get(uuid) != placement) {
cancel(); return;
}
if (placement.getStation().hasBlueprint()) {
activePlacements.remove(uuid);
cancel(); return;
}
Location playerLoc = player.getLocation();
if (playerLoc.distanceSquared(stationLoc) > Math.pow(Cache.constructionDistance, 2)) return;
if (!java.util.Objects.equals(playerLoc.getWorld(), stationLoc.getWorld())
|| playerLoc.distanceSquared(stationLoc) > Math.pow(Cache.constructionDistance, 2)) return;

drawParticleLine(stationLoc.clone().add(0.5, 1, 0.5),
playerLoc.clone().add(0, 1.5, 0));
Expand All @@ -314,7 +313,7 @@ public void run() {
new BukkitRunnable() {
@Override
public void run() {
if (activePlacements.containsKey(uuid)) {
if (activePlacements.get(uuid) == placement) {
activePlacements.remove(uuid);
player.sendMessage("§cVehicle placement cancelled (timeout).");
}
Expand Down Expand Up @@ -353,6 +352,7 @@ public void onPlayerQuit(PlayerQuitEvent e) {

@EventHandler
public void onBlockBreak(BlockBreakEvent e) {
if (e.isCancelled()) return;
Block block = e.getBlock();
Location loc = block.getLocation();

Expand All @@ -363,6 +363,7 @@ public void onBlockBreak(BlockBreakEvent e) {

@EventHandler
public void onFurnitureBreak(FurnitureBreakEvent e) {
if (e.isCancelled()) return;
Location loc = e.getBukkitEntity().getLocation().getBlock().getLocation();

if (!stations.containsKey(loc)) return;
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/categories.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Blueprint categories are configured here. Existing server configuration is preserved.
{}
2 changes: 2 additions & 0 deletions src/main/resources/stations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Construction stations are configured here. Existing server configuration is preserved.
{}
22 changes: 22 additions & 0 deletions src/test/java/net/tfminecraft/vfbuilders/DatabaseFixture.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package net.tfminecraft.vfbuilders;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

/** One data directory per JVM, matching Database's static path lifetime. */
final class DatabaseFixture {
private static final Path ROOT;

static {
try {
ROOT = Files.createTempDirectory("vfbuilders-database-");
} catch (IOException e) {
throw new ExceptionInInitializerError(e);
}
}

static Path folder() throws IOException {
return Files.createDirectories(ROOT);
}
}
Loading