diff --git a/README.md b/README.md
index 7386d12b..c95bb5ee 100644
--- a/README.md
+++ b/README.md
@@ -13,7 +13,7 @@ It is lightweight and fast (using [packets](https://wiki.vg/Protocol)).
PlaceholderAPI and MiniPlaceholders is supported.
-**Only for minecraft server versions 1.19.4 - 1.20.4**
+**Only for minecraft server versions 1.19.4 - 1.20.6**
_Using [paper](https://papermc.io/downloads) is highly recommended_
## Get the plugin
diff --git a/api/build.gradle.kts b/api/build.gradle.kts
index a95fdeb9..ee6d28f6 100644
--- a/api/build.gradle.kts
+++ b/api/build.gradle.kts
@@ -1,7 +1,7 @@
plugins {
id("java-library")
id("maven-publish")
- id("com.github.johnrengelman.shadow") version "8.1.1"
+ id("io.github.goooler.shadow") version "8.1.7"
}
dependencies {
diff --git a/build.gradle.kts b/build.gradle.kts
index 404ce77d..21d225fe 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -5,8 +5,8 @@ plugins {
id("java-library")
id("maven-publish")
- id("xyz.jpenilla.run-paper") version "2.2.2"
- id("com.github.johnrengelman.shadow") version "8.1.1"
+ id("xyz.jpenilla.run-paper") version "2.2.4"
+ id("io.github.goooler.shadow") version "8.1.7"
}
runPaper.folia.registerTask()
@@ -14,14 +14,15 @@ runPaper.folia.registerTask()
allprojects {
group = "de.oliver"
val buildId = System.getenv("BUILD_ID")
- version = "2.0.6" + (if (buildId != null) ".$buildId" else "")
+ version = "2.1.0-SNAPSHOT" + (if (buildId != null) ".$buildId" else "")
description = "Simple, lightweight and fast hologram plugin using display entities"
repositories {
mavenCentral()
- maven(url = "https://papermc.io/repo/repository/maven-public/")
+ maven(url = "https://repo.papermc.io/repository/maven-public/")
+ maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
maven(url = "https://repo.fancyplugins.de/snapshots")
maven(url = "https://repo.smrt-1.com/releases")
@@ -33,6 +34,7 @@ dependencies {
compileOnly("io.papermc.paper:paper-api:${findProperty("minecraftVersion")}-R0.1-SNAPSHOT")
implementation(project(":api"))
+ implementation(project(":implementation_1_20_6", configuration = "reobf"))
implementation(project(":implementation_1_20_4", configuration = "reobf"))
implementation(project(":implementation_1_20_2", configuration = "reobf"))
implementation(project(":implementation_1_20_1", configuration = "reobf"))
@@ -54,20 +56,21 @@ tasks {
dependsOn(":api:shadowJar")
- relocate("me.dave.chatcolorhandler", "de.oliver.fancyholograms.libs.chatcolorhandler")
+// relocate("me.dave.chatcolorhandler", "de.oliver.fancyholograms.libs.chatcolorhandler")
relocate("io.sentry", "de.oliver.fancyholograms.libs.sentry")
}
runServer {
- minecraftVersion(findProperty("minecraftVersion").toString())
+// minecraftVersion(findProperty("minecraftVersion").toString())
+ minecraftVersion("1.20.6")
downloadPlugins {
- hangar("FancyNpcs", findProperty("fancyNpcsVersion").toString())
- hangar("PlaceholderAPI", "2.11.5")
- modrinth("miniplaceholders", "M6gjRuIx")
-
- hangar("ViaVersion", "4.9.3-SNAPSHOT+216")
- hangar("ViaBackwards", "4.9.2-SNAPSHOT+131")
+// hangar("FancyNpcs", findProperty("fancyNpcsVersion").toString())
+// hangar("PlaceholderAPI", "2.11.5")
+// modrinth("miniplaceholders", "M6gjRuIx")
+//
+// hangar("ViaVersion", "4.9.3-SNAPSHOT+216")
+// hangar("ViaBackwards", "4.9.2-SNAPSHOT+131")
}
}
@@ -124,11 +127,17 @@ tasks {
expand(props)
}
}
+
+ compileJava {
+ options.encoding = Charsets.UTF_8.name() // We want UTF-8 for everything
+ options.release = 21
+ }
+
}
java {
toolchain {
- languageVersion.set(JavaLanguageVersion.of(17))
+ languageVersion.set(JavaLanguageVersion.of(21))
}
}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 59bc51a2..48c0a02c 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/implementation_1_19_4/build.gradle.kts b/implementation_1_19_4/build.gradle.kts
index 6444d1b3..826693b9 100644
--- a/implementation_1_19_4/build.gradle.kts
+++ b/implementation_1_19_4/build.gradle.kts
@@ -1,6 +1,6 @@
plugins {
id("java-library")
- id("io.papermc.paperweight.userdev") version "1.5.11"
+ id("io.papermc.paperweight.userdev") version "1.6.2"
}
diff --git a/implementation_1_20_1/build.gradle.kts b/implementation_1_20_1/build.gradle.kts
index b860f8d6..a9a0b15f 100644
--- a/implementation_1_20_1/build.gradle.kts
+++ b/implementation_1_20_1/build.gradle.kts
@@ -1,6 +1,6 @@
plugins {
id("java-library")
- id("io.papermc.paperweight.userdev") version "1.5.11"
+ id("io.papermc.paperweight.userdev") version "1.6.2"
}
diff --git a/implementation_1_20_2/build.gradle.kts b/implementation_1_20_2/build.gradle.kts
index 7531bd95..02ce7b72 100644
--- a/implementation_1_20_2/build.gradle.kts
+++ b/implementation_1_20_2/build.gradle.kts
@@ -1,6 +1,6 @@
plugins {
id("java-library")
- id("io.papermc.paperweight.userdev") version "1.5.11"
+ id("io.papermc.paperweight.userdev") version "1.6.2"
}
diff --git a/implementation_1_20_4/build.gradle.kts b/implementation_1_20_4/build.gradle.kts
index e5a80b9c..5a476c9d 100644
--- a/implementation_1_20_4/build.gradle.kts
+++ b/implementation_1_20_4/build.gradle.kts
@@ -1,6 +1,6 @@
plugins {
id("java-library")
- id("io.papermc.paperweight.userdev") version "1.5.11"
+ id("io.papermc.paperweight.userdev") version "1.6.2"
}
diff --git a/implementation_1_20_6/build.gradle.kts b/implementation_1_20_6/build.gradle.kts
new file mode 100644
index 00000000..7fb5350f
--- /dev/null
+++ b/implementation_1_20_6/build.gradle.kts
@@ -0,0 +1,35 @@
+plugins {
+ id("java-library")
+ id("io.papermc.paperweight.userdev") version "1.6.2"
+}
+
+
+val minecraftVersion = "1.20.6"
+
+paperweight.reobfArtifactConfiguration = io.papermc.paperweight.userdev.ReobfArtifactConfiguration.MOJANG_PRODUCTION
+
+
+dependencies {
+ paperweight.paperDevBundle("$minecraftVersion-R0.1-SNAPSHOT")
+
+ implementation(project(":api"))
+ implementation("de.oliver:FancyLib:${findProperty("fancyLibVersion")}")
+ compileOnly("com.viaversion:viaversion-api:${findProperty("viaversionVersion")}")
+}
+
+
+tasks {
+ named("assemble") {
+ dependsOn(named("reobfJar"))
+ }
+
+ javadoc {
+ options.encoding = Charsets.UTF_8.name()
+ }
+
+ compileJava {
+ options.encoding = Charsets.UTF_8.name()
+
+ options.release.set(21)
+ }
+}
\ No newline at end of file
diff --git a/implementation_1_20_6/src/main/java/de/oliver/fancyholograms/version/Hologram1_20_6.java b/implementation_1_20_6/src/main/java/de/oliver/fancyholograms/version/Hologram1_20_6.java
new file mode 100644
index 00000000..83440d71
--- /dev/null
+++ b/implementation_1_20_6/src/main/java/de/oliver/fancyholograms/version/Hologram1_20_6.java
@@ -0,0 +1,267 @@
+package de.oliver.fancyholograms.version;
+
+import com.mojang.math.Transformation;
+import com.viaversion.viaversion.api.Via;
+import de.oliver.fancyholograms.api.FancyHologramsPlugin;
+import de.oliver.fancyholograms.api.Hologram;
+import de.oliver.fancyholograms.api.data.BlockHologramData;
+import de.oliver.fancyholograms.api.data.HologramData;
+import de.oliver.fancyholograms.api.data.ItemHologramData;
+import de.oliver.fancyholograms.api.data.TextHologramData;
+import de.oliver.fancyholograms.api.events.HologramHideEvent;
+import de.oliver.fancyholograms.api.events.HologramShowEvent;
+import de.oliver.fancylib.ReflectionUtils;
+import io.papermc.paper.adventure.PaperAdventure;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
+import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket;
+import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
+import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
+import net.minecraft.network.syncher.EntityDataAccessor;
+import net.minecraft.network.syncher.SynchedEntityData;
+import net.minecraft.network.syncher.SynchedEntityData.DataValue;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.level.ServerPlayer;
+import net.minecraft.util.Brightness;
+import net.minecraft.world.entity.Display;
+import net.minecraft.world.entity.Display.TextDisplay;
+import net.minecraft.world.entity.EntityType;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.level.block.Block;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.bukkit.craftbukkit.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.joml.Quaternionf;
+
+import java.util.ArrayList;
+
+import static de.oliver.fancylib.ReflectionUtils.getValue;
+
+public final class Hologram1_20_6 extends Hologram {
+
+ @Nullable
+ private Display display;
+
+ public Hologram1_20_6(@NotNull final HologramData data) {
+ super(data);
+ }
+
+ @Override
+ public org.bukkit.entity.@Nullable Display getDisplayEntity() {
+ if (display == null) return null;
+ return (org.bukkit.entity.Display) display.getBukkitEntity();
+ }
+
+ @Override
+ public void create() {
+ final var location = data.getDisplayData().getLocation();
+ if (location == null || location.getWorld() == null) {
+ return; // no location data, cannot be created
+ }
+
+ ServerLevel world = ((CraftWorld) location.getWorld()).getHandle();
+
+ switch (data.getType()) {
+ case TEXT -> this.display = new Display.TextDisplay(EntityType.TEXT_DISPLAY, world);
+ case BLOCK -> this.display = new Display.BlockDisplay(EntityType.BLOCK_DISPLAY, world);
+ case ITEM -> this.display = new Display.ItemDisplay(EntityType.ITEM_DISPLAY, world);
+ }
+
+ final var DATA_TRANSFORMATION_INTERPOLATION_DURATION_ID = ReflectionUtils.getStaticValue(Display.class, MappingKeys1_20_6.DISPLAY__DATA_TRANSFORMATION_INTERPOLATION_DURATION_ID.getMapping());
+ display.getEntityData().set((EntityDataAccessor) DATA_TRANSFORMATION_INTERPOLATION_DURATION_ID, 1);
+
+ final var DATA_TRANSFORMATION_INTERPOLATION_START_DELTA_TICKS_ID = ReflectionUtils.getStaticValue(Display.class, MappingKeys1_20_6.DISPLAY__DATA_TRANSFORMATION_INTERPOLATION_START_DELTA_TICKS_ID.getMapping());
+ display.getEntityData().set((EntityDataAccessor) DATA_TRANSFORMATION_INTERPOLATION_START_DELTA_TICKS_ID, 0);
+
+ updateHologram();
+ }
+
+ @Override
+ public void delete() {
+ this.display = null;
+ }
+
+ @Override
+ public void update() {
+ final var display = this.display;
+ if (display == null) {
+ return; // doesn't exist, nothing to update
+ }
+
+ // location data
+ final var location = data.getDisplayData().getLocation();
+ if (location == null || location.getWorld() == null || !location.isWorldLoaded()) {
+ return;
+ } else {
+ display.setPosRaw(location.x(), location.y(), location.z());
+ display.setYRot(location.getYaw());
+ display.setXRot(location.getPitch());
+ }
+
+
+ // billboard data
+ display.setBillboardConstraints(switch (data.getDisplayData().getBillboard()) {
+ case FIXED -> Display.BillboardConstraints.FIXED;
+ case VERTICAL -> Display.BillboardConstraints.VERTICAL;
+ case HORIZONTAL -> Display.BillboardConstraints.HORIZONTAL;
+ case CENTER -> Display.BillboardConstraints.CENTER;
+ });
+
+
+ if (display instanceof TextDisplay textDisplay && data.getTypeData() instanceof TextHologramData textData) {
+ // line width
+ final var DATA_LINE_WIDTH_ID = ReflectionUtils.getStaticValue(TextDisplay.class, MappingKeys1_20_6.TEXT_DISPLAY__DATA_LINE_WIDTH_ID.getMapping());
+ display.getEntityData().set((EntityDataAccessor) DATA_LINE_WIDTH_ID, Hologram.LINE_WIDTH);
+
+ // background
+ final var DATA_BACKGROUND_COLOR_ID = ReflectionUtils.getStaticValue(TextDisplay.class, MappingKeys1_20_6.TEXT_DISPLAY__DATA_BACKGROUND_COLOR_ID.getMapping());
+
+ final var background = textData.getBackground();
+ if (background == null) {
+ display.getEntityData().set((EntityDataAccessor) DATA_BACKGROUND_COLOR_ID, TextDisplay.INITIAL_BACKGROUND);
+ } else if (background == Hologram.TRANSPARENT) {
+ display.getEntityData().set((EntityDataAccessor) DATA_BACKGROUND_COLOR_ID, 0);
+ } else {
+ display.getEntityData().set((EntityDataAccessor) DATA_BACKGROUND_COLOR_ID, background.value() | 0xC8000000);
+ }
+
+ // text shadow
+ if (textData.isTextShadow()) {
+ textDisplay.setFlags((byte) (textDisplay.getFlags() | TextDisplay.FLAG_SHADOW));
+ } else {
+ textDisplay.setFlags((byte) (textDisplay.getFlags() & ~TextDisplay.FLAG_SHADOW));
+ }
+
+ // see through
+ if (textData.isSeeThrough()) {
+ textDisplay.setFlags((byte) (textDisplay.getFlags() | TextDisplay.FLAG_SEE_THROUGH));
+ } else {
+ textDisplay.setFlags((byte) (textDisplay.getFlags() & ~TextDisplay.FLAG_SEE_THROUGH));
+ }
+
+ // text alignment
+ if (textData.getTextAlignment() == org.bukkit.entity.TextDisplay.TextAlignment.LEFT) {
+ textDisplay.setFlags((byte) (textDisplay.getFlags() | TextDisplay.FLAG_ALIGN_LEFT));
+ } else {
+ textDisplay.setFlags((byte) (textDisplay.getFlags() & ~TextDisplay.FLAG_ALIGN_LEFT));
+ }
+
+ if (textData.getTextAlignment() == org.bukkit.entity.TextDisplay.TextAlignment.RIGHT) {
+ textDisplay.setFlags((byte) (textDisplay.getFlags() | TextDisplay.FLAG_ALIGN_RIGHT));
+ } else {
+ textDisplay.setFlags((byte) (textDisplay.getFlags() & ~TextDisplay.FLAG_ALIGN_RIGHT));
+ }
+
+ } else if (display instanceof Display.ItemDisplay itemDisplay && data.getTypeData() instanceof ItemHologramData itemData) {
+ // item
+ itemDisplay.setItemStack(ItemStack.fromBukkitCopy(itemData.getItem()));
+
+ } else if (display instanceof Display.BlockDisplay blockDisplay && data.getTypeData() instanceof BlockHologramData blockData) {
+ Block block = BuiltInRegistries.BLOCK.get(ResourceLocation.of("minecraft:" + blockData.getBlock().name().toLowerCase(), ':'));
+ blockDisplay.setBlockState(block.defaultBlockState());
+ }
+
+ // brightness
+ if (data.getDisplayData().getBrightness() != null) {
+ display.setBrightnessOverride(new Brightness(data.getDisplayData().getBrightness().getBlockLight(),
+ data.getDisplayData().getBrightness().getSkyLight()));
+ }
+
+ // entity scale AND MORE!
+ display.setTransformation(new Transformation(
+ data.getDisplayData().getTranslation(),
+ new Quaternionf(),
+ data.getDisplayData().getScale(),
+ new Quaternionf())
+ );
+
+
+ // entity shadow
+ display.setShadowRadius(data.getDisplayData().getShadowRadius());
+ display.setShadowStrength(data.getDisplayData().getShadowStrength());
+ }
+
+
+ @Override
+ public boolean show(@NotNull final Player player) {
+ if (!new HologramShowEvent(this, player).callEvent()) {
+ return false;
+ }
+
+ if (this.display == null) {
+ create(); // try to create it if it doesn't exist every time
+ }
+
+ final var display = this.display;
+ if (display == null) {
+ return false; // could not be created, nothing to show
+ }
+
+ if (!data.getDisplayData().getLocation().getWorld().getName().equals(player.getLocation().getWorld().getName())) {
+ return false;
+ }
+
+ ServerPlayer serverPlayer = ((CraftPlayer) player).getHandle();
+
+ // TODO: cache player protocol version
+ final var protocolVersion = FancyHologramsPlugin.get().isUsingViaVersion() ? Via.getAPI().getPlayerVersion(player) : MINIMUM_PROTOCOL_VERSION;
+ if (protocolVersion < MINIMUM_PROTOCOL_VERSION) {
+ return false;
+ }
+
+ serverPlayer.connection.send(new ClientboundAddEntityPacket(display));
+ this.shown.add(player.getUniqueId());
+ refreshHologram(player);
+
+ return true;
+ }
+
+ @Override
+ public boolean hide(@NotNull final Player player) {
+ if (!new HologramHideEvent(this, player).callEvent()) {
+ return false;
+ }
+
+ final var display = this.display;
+ if (display == null) {
+ return false; // doesn't exist, nothing to hide
+ }
+
+ ((CraftPlayer) player).getHandle().connection.send(new ClientboundRemoveEntitiesPacket(display.getId()));
+
+ this.shown.remove(player.getUniqueId());
+ return true;
+ }
+
+
+ @Override
+ public void refresh(@NotNull final Player player) {
+ final var display = this.display;
+ if (display == null) {
+ return; // doesn't exist, nothing to refresh
+ }
+
+ if (!isShown(player)) {
+ return;
+ }
+
+ ((CraftPlayer) player).getHandle().connection.send(new ClientboundTeleportEntityPacket(display));
+
+ if (display instanceof TextDisplay textDisplay) {
+ textDisplay.setText(PaperAdventure.asVanilla(getShownText(player)));
+ }
+
+ final var values = new ArrayList>();
+
+ //noinspection unchecked
+ for (final var item : ((SynchedEntityData.DataItem>[]) getValue(display.getEntityData(), "itemsById"))) {
+ values.add(item.value());
+ }
+
+ ((CraftPlayer) player).getHandle().connection.send(new ClientboundSetEntityDataPacket(display.getId(), values));
+ }
+
+}
diff --git a/implementation_1_20_6/src/main/java/de/oliver/fancyholograms/version/MappingKeys1_20_6.java b/implementation_1_20_6/src/main/java/de/oliver/fancyholograms/version/MappingKeys1_20_6.java
new file mode 100644
index 00000000..afc1a228
--- /dev/null
+++ b/implementation_1_20_6/src/main/java/de/oliver/fancyholograms/version/MappingKeys1_20_6.java
@@ -0,0 +1,20 @@
+package de.oliver.fancyholograms.version;
+
+public enum MappingKeys1_20_6 {
+
+ DISPLAY__DATA_TRANSFORMATION_INTERPOLATION_DURATION_ID("DATA_TRANSFORMATION_INTERPOLATION_DURATION_ID"),
+ DISPLAY__DATA_TRANSFORMATION_INTERPOLATION_START_DELTA_TICKS_ID("DATA_TRANSFORMATION_INTERPOLATION_START_DELTA_TICKS_ID"),
+ TEXT_DISPLAY__DATA_LINE_WIDTH_ID("DATA_LINE_WIDTH_ID"),
+ TEXT_DISPLAY__DATA_BACKGROUND_COLOR_ID("DATA_BACKGROUND_COLOR_ID"),
+ ;
+
+ private final String mapping;
+
+ MappingKeys1_20_6(String mapping) {
+ this.mapping = mapping;
+ }
+
+ public String getMapping() {
+ return mapping;
+ }
+}
diff --git a/settings.gradle.kts b/settings.gradle.kts
index 854f7dd2..58a8d006 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -1,6 +1,7 @@
rootProject.name = "FancyHolograms"
include("api")
+include("implementation_1_20_6")
include("implementation_1_20_4")
include("implementation_1_20_2")
include("implementation_1_20_1")
diff --git a/src/main/java/de/oliver/fancyholograms/FancyHolograms.java b/src/main/java/de/oliver/fancyholograms/FancyHolograms.java
index 01d22150..432b3334 100644
--- a/src/main/java/de/oliver/fancyholograms/FancyHolograms.java
+++ b/src/main/java/de/oliver/fancyholograms/FancyHolograms.java
@@ -9,10 +9,7 @@
import de.oliver.fancyholograms.listeners.NpcListener;
import de.oliver.fancyholograms.listeners.PlayerListener;
import de.oliver.fancyholograms.storage.FlatFileHologramStorage;
-import de.oliver.fancyholograms.version.Hologram1_19_4;
-import de.oliver.fancyholograms.version.Hologram1_20_1;
-import de.oliver.fancyholograms.version.Hologram1_20_2;
-import de.oliver.fancyholograms.version.Hologram1_20_4;
+import de.oliver.fancyholograms.version.*;
import de.oliver.fancylib.FancyLib;
import de.oliver.fancylib.Metrics;
import de.oliver.fancylib.VersionConfig;
@@ -41,7 +38,7 @@
public final class FancyHolograms extends JavaPlugin implements FancyHologramsPlugin {
- public static final String[] SUPPORTED_VERSIONS = {"1.19.4", "1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4"};
+ public static final String[] SUPPORTED_VERSIONS = {"1.19.4", "1.20", "1.20.1", "1.20.2", "1.20.3", "1.20.4", "1.20.5", "1.20.6"};
@Nullable
@@ -204,6 +201,7 @@ public ExecutorService getFileStorageExecutor() {
final var version = Bukkit.getMinecraftVersion();
return switch (version) {
+ case "1.20.5", "1.20.6" -> Hologram1_20_6::new;
case "1.20.3", "1.20.4" -> Hologram1_20_4::new;
case "1.20.2" -> Hologram1_20_2::new;
case "1.20", "1.20.1" -> Hologram1_20_1::new;