ci: multi-version build matrix + multi-MC compat layer
- Parametrize build: -PmcVersion / -PapiCoordinate / -PjavaRelease / -PapiVersion; jar name includes mc version, plugin.yml api-version parametrized - Compat (reflection): sendBlockChanges / getClientViewDistance / getStructureManager (Paper 1.19.3+); particle()/potionEffect() name lookup for 1.21 renames (BLOCK_CRACK->BLOCK, REDSTONE->DUST, FIREWORKS_SPARK-> FIREWORK, JUMP->JUMP_BOOST, SLOW->SLOWNESS); drop CHERRY materials (1.20+) - Fix Java 17 'unconditional instanceof' in JoinCommandExecutor - capabilitiesResolution for org.bukkit:bukkit capability conflict on newer paper-api artifacts - .github/workflows/build.yml: matrix for 1.19.2/1.20.1 (Java 17), 1.21/1.21.11 (Java 21), 26.1.2/26.2 (Java 25) + quality gate - Verified locally: default + 1.19.2/1.20.1/1.21/1.21.11 build; 26.x deps resolve
This commit is contained in:
parent
3b5d3d97d5
commit
c0ca488b9d
13 changed files with 260 additions and 36 deletions
65
.github/workflows/build.yml
vendored
Normal file
65
.github/workflows/build.yml
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Build a shadowJar per Minecraft version against the matching Paper API.
|
||||||
|
build-matrix:
|
||||||
|
name: Build mc-${{ matrix.mc }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
# 1.19.2 — Java 17
|
||||||
|
- { mc: "1.19.2", coordinate: "io.papermc.paper:paper-api:1.19.2-R0.1-SNAPSHOT", java: 17, release: 17, api: "1.19" }
|
||||||
|
# 1.20.1 — Java 17
|
||||||
|
- { mc: "1.20.1", coordinate: "io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT", java: 17, release: 17, api: "1.20" }
|
||||||
|
# 1.21 — Java 21
|
||||||
|
- { mc: "1.21", coordinate: "io.papermc.paper:paper-api:1.21-R0.1-SNAPSHOT", java: 21, release: 21, api: "1.20" }
|
||||||
|
# 1.21.11 — Java 21
|
||||||
|
- { mc: "1.21.11", coordinate: "io.papermc.paper:paper-api:1.21.11-R0.1-SNAPSHOT", java: 21, release: 21, api: "1.21" }
|
||||||
|
# 26.1.2 / 26.2 — Java 25 (new-style paper-api build artifacts; may need a
|
||||||
|
# Gradle wrapper >= 8.14 if the daemon rejects Java 25 — bump if so).
|
||||||
|
- { mc: "26.1.2", coordinate: "io.papermc.paper:paper-api:26.1.2.build.74-stable", java: 25, release: 25, api: "1.21" }
|
||||||
|
- { mc: "26.2", coordinate: "io.papermc.paper:paper-api:26.2.build.97-stable", java: 25, release: 25, api: "1.21" }
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: ${{ matrix.java }}
|
||||||
|
- name: Build jar (mc ${{ matrix.mc }})
|
||||||
|
run: >
|
||||||
|
./gradlew --no-daemon shadowJar
|
||||||
|
-PmcVersion=${{ matrix.mc }}
|
||||||
|
-PapiCoordinate='${{ matrix.coordinate }}'
|
||||||
|
-PjavaRelease=${{ matrix.release }}
|
||||||
|
-PapiVersion=${{ matrix.api }}
|
||||||
|
- name: Upload jar
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: LoParkour-${{ matrix.mc }}
|
||||||
|
path: build/libs/LoParkour-*.jar
|
||||||
|
|
||||||
|
# Full quality gate (checkstyle + PMD + tests) on the default build.
|
||||||
|
quality:
|
||||||
|
name: Quality gate (default 1.20.4)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-java@v4
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: 21
|
||||||
|
- name: Full build
|
||||||
|
run: ./gradlew --no-daemon build
|
||||||
|
- name: Upload default jar
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: LoParkour-default-1.20.4
|
||||||
|
path: build/libs/LoParkour-*.jar
|
||||||
|
|
@ -10,15 +10,16 @@ group = "dev.loki"
|
||||||
version = "1.3.3"
|
version = "1.3.3"
|
||||||
description = "LoParkour - Advanced parkour plugin for Minecraft"
|
description = "LoParkour - Advanced parkour plugin for Minecraft"
|
||||||
|
|
||||||
java {
|
// Multi-version build: pass -PmcVersion / -PapiCoordinate / -PjavaRelease / -PapiVersion
|
||||||
toolchain {
|
// to build the same source against a different Paper API (e.g. GitHub Actions matrix).
|
||||||
languageVersion = JavaLanguageVersion.of(21)
|
val mcVersion: String = providers.gradleProperty("mcVersion").getOrElse("1.20.4")
|
||||||
}
|
val apiCoordinate: String = providers.gradleProperty("apiCoordinate")
|
||||||
}
|
.getOrElse("org.spigotmc:spigot-api:$mcVersion-R0.1-SNAPSHOT")
|
||||||
|
val javaRelease: Int = providers.gradleProperty("javaRelease").map { it.toInt() }.getOrElse(21)
|
||||||
|
val apiVersion: String = providers.gradleProperty("apiVersion").getOrElse("1.20")
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|
||||||
maven {
|
maven {
|
||||||
name = "papermc"
|
name = "papermc"
|
||||||
url = uri("https://repo.papermc.io/repository/maven-public/")
|
url = uri("https://repo.papermc.io/repository/maven-public/")
|
||||||
|
|
@ -76,9 +77,29 @@ repositories {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve the org.bukkit:bukkit capability conflict that newer paper-api artifacts
|
||||||
|
// introduce (they both bundle the bukkit classes and declare the capability).
|
||||||
|
configurations.configureEach {
|
||||||
|
resolutionStrategy.capabilitiesResolution.withCapability("org.bukkit:bukkit") {
|
||||||
|
selectHighestVersion()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Spigot API
|
// Spigot/Paper API (overridable per MC version via -PapiCoordinate).
|
||||||
compileOnly("org.spigotmc:spigot-api:1.20.4-R0.1-SNAPSHOT")
|
// Newer paper-api artifacts declare a conflicting org.bukkit:bukkit capability
|
||||||
|
// (they bundle the classes themselves) — drop the duplicate transitive.
|
||||||
|
if (apiCoordinate.startsWith("io.papermc.paper:paper-api")) {
|
||||||
|
val (apiGroup, apiName, apiVersion) = apiCoordinate.split(":")
|
||||||
|
compileOnly(mapOf(
|
||||||
|
"group" to apiGroup,
|
||||||
|
"name" to apiName,
|
||||||
|
"version" to apiVersion,
|
||||||
|
"exclude" to mapOf("group" to "org.bukkit", "module" to "bukkit")
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
compileOnly(apiCoordinate)
|
||||||
|
}
|
||||||
|
|
||||||
// Adventure API (for Component)
|
// Adventure API (for Component)
|
||||||
compileOnly("net.kyori:adventure-api:4.14.0")
|
compileOnly("net.kyori:adventure-api:4.14.0")
|
||||||
|
|
@ -116,7 +137,7 @@ dependencies {
|
||||||
|
|
||||||
tasks.shadowJar {
|
tasks.shadowJar {
|
||||||
archiveClassifier.set("")
|
archiveClassifier.set("")
|
||||||
archiveFileName.set("LoParkour-${project.version}.jar")
|
archiveFileName.set("LoParkour-${project.version}-mc${mcVersion}.jar")
|
||||||
|
|
||||||
// Relocate shaded dependencies
|
// Relocate shaded dependencies
|
||||||
relocate("dev.lolib", "dev.loki.loparkour.lib.lolib")
|
relocate("dev.lolib", "dev.loki.loparkour.lib.lolib")
|
||||||
|
|
@ -141,7 +162,8 @@ tasks.withType<ProcessResources> {
|
||||||
filesMatching(listOf("plugin.yml", "config.yml")) {
|
filesMatching(listOf("plugin.yml", "config.yml")) {
|
||||||
expand(
|
expand(
|
||||||
"version" to project.version,
|
"version" to project.version,
|
||||||
"description" to project.description
|
"description" to project.description,
|
||||||
|
"apiVersion" to apiVersion
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -152,7 +174,7 @@ tasks.withType<Test> {
|
||||||
|
|
||||||
tasks.withType<JavaCompile> {
|
tasks.withType<JavaCompile> {
|
||||||
options.encoding = "UTF-8"
|
options.encoding = "UTF-8"
|
||||||
options.release = 21
|
options.release = javaRelease
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<Javadoc> {
|
tasks.withType<Javadoc> {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,8 @@ public class JoinCommandExecutor {
|
||||||
if (!player.cooldown(sender, "join", COOLDOWN_MS) || !ParkourOption.JOIN.mayPerform(p)) {
|
if (!player.cooldown(sender, "join", COOLDOWN_MS) || !ParkourOption.JOIN.mayPerform(p)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Registry.getMode(arg) instanceof Mode mode) {
|
final Mode mode = Registry.getMode(arg);
|
||||||
|
if (mode != null) {
|
||||||
mode.create(p);
|
mode.create(p);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package dev.loki.loparkour.elytra.generator;
|
package dev.loki.loparkour.elytra.generator;
|
||||||
|
|
||||||
import dev.loki.loparkour.elytra.section.ElytraSection;
|
import dev.loki.loparkour.elytra.section.ElytraSection;
|
||||||
|
import dev.loki.loparkour.util.misc.Compat;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.Particle;
|
import org.bukkit.Particle;
|
||||||
|
|
@ -23,6 +24,7 @@ final class ElytraBoost {
|
||||||
private static final float BLAST_PITCH = 1.4f;
|
private static final float BLAST_PITCH = 1.4f;
|
||||||
private static final double TRAIL_OFFSET = 0.2;
|
private static final double TRAIL_OFFSET = 0.2;
|
||||||
private static final double TRAIL_SPEED = 0.02;
|
private static final double TRAIL_SPEED = 0.02;
|
||||||
|
private static final Particle SPARK = Compat.particle("FIREWORKS_SPARK", "FIREWORK");
|
||||||
private int boostedSectionIdx = Integer.MIN_VALUE;
|
private int boostedSectionIdx = Integer.MIN_VALUE;
|
||||||
|
|
||||||
void tick(Player player, ElytraSection section, int idx) {
|
void tick(Player player, ElytraSection section, int idx) {
|
||||||
|
|
@ -53,7 +55,9 @@ final class ElytraBoost {
|
||||||
.add(direction.clone().multiply(INITIAL_PUSH));
|
.add(direction.clone().multiply(INITIAL_PUSH));
|
||||||
player.setVelocity(velocity);
|
player.setVelocity(velocity);
|
||||||
final Location loc = player.getLocation();
|
final Location loc = player.getLocation();
|
||||||
loc.getWorld().spawnParticle(Particle.FIREWORKS_SPARK, loc, FIREWORK_COUNT, BURST_OFFSET, BURST_OFFSET, BURST_OFFSET, BURST_SPEED);
|
if (SPARK != null) {
|
||||||
|
loc.getWorld().spawnParticle(SPARK, loc, FIREWORK_COUNT, BURST_OFFSET, BURST_OFFSET, BURST_OFFSET, BURST_SPEED);
|
||||||
|
}
|
||||||
loc.getWorld().playSound(loc, Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1.0f, 1.0f);
|
loc.getWorld().playSound(loc, Sound.ENTITY_FIREWORK_ROCKET_LAUNCH, 1.0f, 1.0f);
|
||||||
loc.getWorld().playSound(loc, Sound.ENTITY_FIREWORK_ROCKET_BLAST, BLAST_VOLUME, BLAST_PITCH);
|
loc.getWorld().playSound(loc, Sound.ENTITY_FIREWORK_ROCKET_BLAST, BLAST_VOLUME, BLAST_PITCH);
|
||||||
}
|
}
|
||||||
|
|
@ -64,9 +68,9 @@ final class ElytraBoost {
|
||||||
player.setGliding(true);
|
player.setGliding(true);
|
||||||
}
|
}
|
||||||
player.setVelocity(player.getVelocity().add(direction.clone().multiply(SUSTAINED_BOOST)));
|
player.setVelocity(player.getVelocity().add(direction.clone().multiply(SUSTAINED_BOOST)));
|
||||||
if (player.getTicksLived() % PARTICLE_EVERY_TICKS == 0) {
|
if (SPARK != null && player.getTicksLived() % PARTICLE_EVERY_TICKS == 0) {
|
||||||
final Location loc = player.getLocation();
|
final Location loc = player.getLocation();
|
||||||
loc.getWorld().spawnParticle(Particle.FIREWORKS_SPARK, loc, FIREWORK_PARTICLE_COUNT,
|
loc.getWorld().spawnParticle(SPARK, loc, FIREWORK_PARTICLE_COUNT,
|
||||||
TRAIL_OFFSET, TRAIL_OFFSET, TRAIL_OFFSET, TRAIL_SPEED);
|
TRAIL_OFFSET, TRAIL_OFFSET, TRAIL_OFFSET, TRAIL_SPEED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import org.bukkit.World;
|
||||||
import org.bukkit.block.BlockState;
|
import org.bukkit.block.BlockState;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
|
import dev.loki.loparkour.util.misc.Compat;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -49,7 +50,7 @@ public class ClientBlockChanger {
|
||||||
public void check(Player player, Material material) {
|
public void check(Player player, Material material) {
|
||||||
final int playerCx = player.getLocation().getChunk().getX();
|
final int playerCx = player.getLocation().getChunk().getX();
|
||||||
|
|
||||||
final int rawVd = player.getClientViewDistance();
|
final int rawVd = Compat.getClientViewDistance(player);
|
||||||
final int vd = rawVd > 0 ? rawVd : Math.max(DEFAULT_MIN_VIEW_DISTANCE, player.getWorld().getViewDistance());
|
final int vd = rawVd > 0 ? rawVd : Math.max(DEFAULT_MIN_VIEW_DISTANCE, player.getWorld().getViewDistance());
|
||||||
final int forwardLimit = playerCx + Math.min(vd, MAX_FORWARD_CHUNKS);
|
final int forwardLimit = playerCx + Math.min(vd, MAX_FORWARD_CHUNKS);
|
||||||
|
|
||||||
|
|
@ -111,7 +112,7 @@ public class ClientBlockChanger {
|
||||||
state.setType(material);
|
state.setType(material);
|
||||||
states.add(state);
|
states.add(state);
|
||||||
}
|
}
|
||||||
player.sendBlockChanges(states);
|
Compat.sendBlockChanges(player, states);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -134,7 +135,7 @@ public class ClientBlockChanger {
|
||||||
state.setType(material);
|
state.setType(material);
|
||||||
states.add(state);
|
states.add(state);
|
||||||
}
|
}
|
||||||
player.sendBlockChanges(states);
|
Compat.sendBlockChanges(player, states);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import dev.loki.loparkour.util.misc.Compat;
|
||||||
import org.apache.commons.math3.analysis.interpolation.SplineInterpolator;
|
import org.apache.commons.math3.analysis.interpolation.SplineInterpolator;
|
||||||
import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
|
import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
|
@ -16,11 +17,8 @@ import org.bukkit.entity.Player;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a section of the total parkour of size KNOTS.
|
* A parkour section of {@value #KNOTS} knots; stores Vectors (no chunk pinning).
|
||||||
*
|
* Display blocks are sent client-side and undone in {@link #clear(Player)}.
|
||||||
* <p>Stores {@link Vector} coordinates instead of Bukkit Block instances to avoid
|
|
||||||
* pinning chunks in memory. Display blocks are sent client-side via
|
|
||||||
* {@link Player#sendBlockChanges} and undone in {@link #clear(Player)}.</p>
|
|
||||||
*/
|
*/
|
||||||
public class ElytraSection {
|
public class ElytraSection {
|
||||||
|
|
||||||
|
|
@ -155,7 +153,7 @@ public class ElytraSection {
|
||||||
state.setType(Material.AIR);
|
state.setType(Material.AIR);
|
||||||
states.add(state);
|
states.add(state);
|
||||||
}
|
}
|
||||||
player.sendBlockChanges(states);
|
Compat.sendBlockChanges(player, states);
|
||||||
}
|
}
|
||||||
localBlocks.clear();
|
localBlocks.clear();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,6 @@ public class BlockSelector {
|
||||||
material == Material.JUNGLE_FENCE ||
|
material == Material.JUNGLE_FENCE ||
|
||||||
material == Material.ACACIA_FENCE ||
|
material == Material.ACACIA_FENCE ||
|
||||||
material == Material.MANGROVE_FENCE ||
|
material == Material.MANGROVE_FENCE ||
|
||||||
material == Material.CHERRY_FENCE ||
|
|
||||||
material == Material.CRIMSON_FENCE ||
|
material == Material.CRIMSON_FENCE ||
|
||||||
material == Material.WARPED_FENCE ||
|
material == Material.WARPED_FENCE ||
|
||||||
material == Material.NETHER_BRICK_FENCE;
|
material == Material.NETHER_BRICK_FENCE;
|
||||||
|
|
@ -137,7 +136,6 @@ public class BlockSelector {
|
||||||
material == Material.JUNGLE_TRAPDOOR ||
|
material == Material.JUNGLE_TRAPDOOR ||
|
||||||
material == Material.ACACIA_TRAPDOOR ||
|
material == Material.ACACIA_TRAPDOOR ||
|
||||||
material == Material.MANGROVE_TRAPDOOR ||
|
material == Material.MANGROVE_TRAPDOOR ||
|
||||||
material == Material.CHERRY_TRAPDOOR ||
|
|
||||||
material == Material.CRIMSON_TRAPDOOR ||
|
material == Material.CRIMSON_TRAPDOOR ||
|
||||||
material == Material.WARPED_TRAPDOOR ||
|
material == Material.WARPED_TRAPDOOR ||
|
||||||
material == Material.IRON_TRAPDOOR;
|
material == Material.IRON_TRAPDOOR;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package dev.loki.loparkour.generator.jump.placement;
|
||||||
|
|
||||||
import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator;
|
import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
|
import dev.loki.loparkour.util.misc.Compat;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.Particle;
|
import org.bukkit.Particle;
|
||||||
|
|
@ -31,11 +32,14 @@ class BlockWorldPlacer {
|
||||||
block.setBlockData(data, false);
|
block.setBlockData(data, false);
|
||||||
|
|
||||||
final Location loc = block.getLocation().add(BLOCK_CENTER_OFFSET, BLOCK_CENTER_OFFSET, BLOCK_CENTER_OFFSET);
|
final Location loc = block.getLocation().add(BLOCK_CENTER_OFFSET, BLOCK_CENTER_OFFSET, BLOCK_CENTER_OFFSET);
|
||||||
|
final Particle particle = Compat.particle("BLOCK_CRACK", "BLOCK");
|
||||||
for (final ParkourPlayer player : generator.getPlayers()) {
|
for (final ParkourPlayer player : generator.getPlayers()) {
|
||||||
|
if (particle != null) {
|
||||||
player.player.spawnParticle(
|
player.player.spawnParticle(
|
||||||
Particle.BLOCK_CRACK, loc, PARTICLE_COUNT,
|
particle, loc, PARTICLE_COUNT,
|
||||||
PARTICLE_OFFSET, PARTICLE_OFFSET, PARTICLE_OFFSET, PARTICLE_EXTRA, data
|
PARTICLE_OFFSET, PARTICLE_OFFSET, PARTICLE_OFFSET, PARTICLE_EXTRA, data
|
||||||
);
|
);
|
||||||
|
}
|
||||||
player.player.playSound(loc, Sound.BLOCK_STONE_PLACE, PLACE_SOUND_VOLUME, 1.0f);
|
player.player.playSound(loc, Sound.BLOCK_STONE_PLACE, PLACE_SOUND_VOLUME, 1.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package dev.loki.loparkour.mode.impl.barrier;
|
||||||
|
|
||||||
import dev.loki.loparkour.generator.core.model.GeneratorState;
|
import dev.loki.loparkour.generator.core.model.GeneratorState;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
|
import dev.loki.loparkour.util.misc.Compat;
|
||||||
import org.bukkit.Color;
|
import org.bukkit.Color;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
|
@ -144,12 +145,15 @@ class BarrierRenderer {
|
||||||
final double length = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
final double length = Math.sqrt(dx * dx + dy * dy + dz * dz);
|
||||||
final int steps = Math.max(1, (int) (length / PARTICLE_STEP));
|
final int steps = Math.max(1, (int) (length / PARTICLE_STEP));
|
||||||
|
|
||||||
|
final Particle dust = Compat.particle("REDSTONE", "DUST");
|
||||||
for (int i = 0; i <= steps; i++) {
|
for (int i = 0; i <= steps; i++) {
|
||||||
final double t = (double) i / steps;
|
final double t = (double) i / steps;
|
||||||
final double px = start.x + dx * t;
|
final double px = start.x + dx * t;
|
||||||
final double py = start.y + dy * t;
|
final double py = start.y + dy * t;
|
||||||
final double pz = start.z + dz * t;
|
final double pz = start.z + dz * t;
|
||||||
player.spawnParticle(Particle.REDSTONE, px, py, pz, 1, options);
|
if (dust != null) {
|
||||||
|
player.spawnParticle(dust, px, py, pz, 1, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ import dev.loki.loparkour.mode.base.Modes;
|
||||||
import dev.loki.loparkour.config.core.Config;
|
import dev.loki.loparkour.config.core.Config;
|
||||||
import dev.loki.loparkour.config.locale.Locales;
|
import dev.loki.loparkour.config.locale.Locales;
|
||||||
import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator;
|
import dev.loki.loparkour.generator.core.coordinator.ParkourGenerator;
|
||||||
|
import dev.loki.loparkour.util.misc.Compat;
|
||||||
import dev.loki.loparkour.leaderboard.core.Leaderboard;
|
import dev.loki.loparkour.leaderboard.core.Leaderboard;
|
||||||
import dev.loki.loparkour.player.core.ParkourPlayer;
|
import dev.loki.loparkour.player.core.ParkourPlayer;
|
||||||
import dev.loki.loparkour.session.core.Session;
|
import dev.loki.loparkour.session.core.Session;
|
||||||
|
|
@ -87,9 +88,9 @@ public class GravityShiftMode implements Mode {
|
||||||
? Config.CONFIG.getInt("modes.gravity-shift.interval") : DEFAULT_INTERVAL;
|
? Config.CONFIG.getInt("modes.gravity-shift.interval") : DEFAULT_INTERVAL;
|
||||||
|
|
||||||
this.effects = new HashMap<>();
|
this.effects = new HashMap<>();
|
||||||
loadEffect("jump-boost", PotionEffectType.JUMP);
|
loadEffect("jump-boost", Compat.potionEffect("JUMP_BOOST", "JUMP"));
|
||||||
loadEffect("speed", PotionEffectType.SPEED);
|
loadEffect("speed", PotionEffectType.SPEED);
|
||||||
loadEffect("slowness", PotionEffectType.SLOW);
|
loadEffect("slowness", Compat.potionEffect("SLOWNESS", "SLOW"));
|
||||||
loadEffect("levitation", PotionEffectType.LEVITATION);
|
loadEffect("levitation", PotionEffectType.LEVITATION);
|
||||||
|
|
||||||
// Initialize jump counts for all players
|
// Initialize jump counts for all players
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import com.sk89q.worldedit.extent.clipboard.Clipboard;
|
||||||
import dev.loki.loparkour.LoParkour;
|
import dev.loki.loparkour.LoParkour;
|
||||||
import dev.loki.loparkour.schematic.schem.SchemLoader;
|
import dev.loki.loparkour.schematic.schem.SchemLoader;
|
||||||
import dev.loki.loparkour.config.core.Config;
|
import dev.loki.loparkour.config.core.Config;
|
||||||
import org.bukkit.Bukkit;
|
import dev.loki.loparkour.util.misc.Compat;
|
||||||
import org.bukkit.configuration.ConfigurationSection;
|
import org.bukkit.configuration.ConfigurationSection;
|
||||||
import org.bukkit.structure.Structure;
|
import org.bukkit.structure.Structure;
|
||||||
import org.bukkit.structure.StructureManager;
|
import org.bukkit.structure.StructureManager;
|
||||||
|
|
@ -93,7 +93,10 @@ public final class SchematicManager {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
final StructureManager structureManager = Bukkit.getStructureManager();
|
final StructureManager structureManager = Compat.getStructureManager();
|
||||||
|
if (structureManager == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
int ok = 0;
|
int ok = 0;
|
||||||
for (final File file : files) {
|
for (final File file : files) {
|
||||||
final String stem = stem(file, ".nbt");
|
final String stem = stem(file, ".nbt");
|
||||||
|
|
|
||||||
123
src/main/java/dev/loki/loparkour/util/misc/Compat.java
Normal file
123
src/main/java/dev/loki/loparkour/util/misc/Compat.java
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
package dev.loki.loparkour.util.misc;
|
||||||
|
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Particle;
|
||||||
|
import org.bukkit.block.BlockState;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
import org.bukkit.structure.StructureManager;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reflection helpers for APIs that do not exist on every Minecraft version
|
||||||
|
* (sendBlockChanges / getClientViewDistance are Paper 1.19.3+; StructureManager
|
||||||
|
* is Bukkit 1.19.3+). Lets the single source tree compile against any Paper API
|
||||||
|
* and degrade gracefully at runtime on older servers.
|
||||||
|
*/
|
||||||
|
public final class Compat {
|
||||||
|
|
||||||
|
private Compat() {
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Method SEND_BLOCK_CHANGES = find(Player.class, "sendBlockChanges", Collection.class);
|
||||||
|
private static final Method CLIENT_VIEW_DISTANCE = find(Player.class, "getClientViewDistance");
|
||||||
|
private static final Method STRUCTURE_MANAGER = findServerStructureManager();
|
||||||
|
|
||||||
|
private static Method findServerStructureManager() {
|
||||||
|
try {
|
||||||
|
return find(Bukkit.getServer().getClass(), "getStructureManager");
|
||||||
|
} catch (IllegalStateException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a batch of client-side block changes. No-op on servers where the
|
||||||
|
* method does not exist (Paper < 1.19.3).
|
||||||
|
*/
|
||||||
|
public static void sendBlockChanges(@Nullable Player player, @Nullable Collection<BlockState> states) {
|
||||||
|
if (player == null || states == null || states.isEmpty() || SEND_BLOCK_CHANGES == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
SEND_BLOCK_CHANGES.invoke(player, states);
|
||||||
|
} catch (ReflectiveOperationException ignored) {
|
||||||
|
// API removed at runtime — degrade gracefully
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Client view distance (Paper), or -1 when unavailable so the caller can
|
||||||
|
* fall back to the world view distance.
|
||||||
|
*/
|
||||||
|
public static int getClientViewDistance(@Nullable Player player) {
|
||||||
|
if (player == null || CLIENT_VIEW_DISTANCE == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return (int) CLIENT_VIEW_DISTANCE.invoke(player);
|
||||||
|
} catch (ReflectiveOperationException ignored) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link Bukkit#getStructureManager()}, or {@code null} on versions without it.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static StructureManager getStructureManager() {
|
||||||
|
if (STRUCTURE_MANAGER == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return (StructureManager) STRUCTURE_MANAGER.invoke(Bukkit.getServer());
|
||||||
|
} catch (ReflectiveOperationException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static Method find(Class<?> type, String name, Class<?>... params) {
|
||||||
|
try {
|
||||||
|
return type.getMethod(name, params);
|
||||||
|
} catch (NoSuchMethodException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a {@link Particle} by trying each candidate name in order. Particle
|
||||||
|
* enum constants were renamed between API versions (e.g. BLOCK_CRACK → BLOCK,
|
||||||
|
* REDSTONE → DUST, FIREWORKS_SPARK → FIREWORK), so pass the old and the new name.
|
||||||
|
* Returns {@code null} when none of the names exist.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static Particle particle(String... candidateNames) {
|
||||||
|
for (final String name : candidateNames) {
|
||||||
|
try {
|
||||||
|
return Particle.valueOf(name);
|
||||||
|
} catch (IllegalArgumentException ignored) {
|
||||||
|
// try next candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves a {@link PotionEffectType} by name (JUMP → JUMP_BOOST, SLOW →
|
||||||
|
* SLOWNESS were renamed in 1.21), or {@code null} when none exists.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static PotionEffectType potionEffect(String... candidateNames) {
|
||||||
|
for (final String name : candidateNames) {
|
||||||
|
final PotionEffectType type = PotionEffectType.getByName(name);
|
||||||
|
if (type != null) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,7 @@ name: 'LoParkour'
|
||||||
description: 'Advanced infinitely generating parkour plugin.'
|
description: 'Advanced infinitely generating parkour plugin.'
|
||||||
author: loki
|
author: loki
|
||||||
version: ${version}
|
version: ${version}
|
||||||
api-version: 1.20
|
api-version: ${apiVersion}
|
||||||
main: dev.loki.loparkour.LoParkour
|
main: dev.loki.loparkour.LoParkour
|
||||||
softdepend: [floodgate, Vault, PlaceholderAPI, Multiverse-Core, VoidGen]
|
softdepend: [floodgate, Vault, PlaceholderAPI, Multiverse-Core, VoidGen]
|
||||||
commands:
|
commands:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue